30 Commits

Author SHA1 Message Date
07cd592566 Mix forward-only and bidirectional n_chase patterns 2025-10-26 22:25:38 +13:00
2228b23a53 Remove blink from random test 2025-10-26 22:12:49 +13:00
1f33fff665 Change random test duration to 1-5 mins 2025-10-26 22:09:38 +13:00
e91b291dc6 Remove off from random test 2025-10-26 22:05:52 +13:00
2db9b3464a Add random test suite 2025-10-26 21:33:31 +13:00
8345b31caf Refactor n_chase pattern for bidirectional movement
- Updated n_chase to support bidirectional movement with n3 and n4 parameters
- n3 controls forward steps per direction change
- n4 controls backward steps per direction change
- Pattern alternates between moving forward n3 steps and backward n4 steps
- Each direction repeats for the specified number of steps before switching
- Added test/9.py with n1=20, n2=20, n3=20, n4=-5 parameters
- Updated to run as a thread-based pattern similar to other patterns
2025-10-26 20:39:28 +13:00
dce2954114 Refactor rainbow pattern to travel along the length of LED strip
- Modified rainbow() to distribute colors along the physical length
- Each LED gets a different hue based on its position
- n1 parameter controls how many times the rainbow cycles (nodes)
- Split rainbow tests into test/rainbow/ directory with 9 numbered test files
- Each test runs forever until interrupted
- Added main.py to run all tests in sequence repeatedly
2025-10-26 20:19:21 +13:00
15626431ce Add settings editor utility 2025-10-26 19:03:00 +13:00
b13ec01561 Add flicker and n_chase patterns, increase test brightness to 255, add test suites 2025-10-26 18:49:45 +13:00
83cb34d6a8 Add sine brightness pattern 2025-10-26 00:15:53 +13:00
c9449a6d86 Add circle loading pattern 2025-10-25 23:28:01 +13:00
059bd41a59 convert blink to use a thread 2025-10-25 19:53:16 +13:00
9fad8b1ae5 Refactor param mapping to dict 2025-10-25 19:01:57 +13:00
ae407ab3aa receiver: no change to protocol; revert temporary color-index handling; radiate uses ticks_us timing; development: ensure dev.py upload workflow retained 2025-10-04 01:10:46 +13:00
e516b49eb8 Add segmented_movement pattern with alternating forward/backward movement
- Add n4 parameter support to main.py ESP NOW receiver
- Implement segmented_movement pattern with configurable parameters:
  * n1: segment length (number of LEDs per segment)
  * n2: spacing between segments
  * n3: forward movement speed (positions per beat)
  * n4: backward movement speed (positions per beat)
- Pattern alternates between forward and backward movement each beat
- If only n3 or n4 is set, moves in that direction every beat
- Draws repeating segments with spacing across entire LED strip
- Add Pipfile script to run dev.py directly with arguments
2025-10-03 19:56:24 +13:00
355d113e32 Fix rainbow pattern synchronization in LED bar
- Use controller's step for synchronization instead of internal step counter
- Rainbow pattern now syncs with controller timing like n_chase pattern
- Prevents rainbow from running independently and out of sync
- Uses beat_index % 256 for full color wheel cycling
2025-09-19 01:29:48 +12:00
d715af4344 Fix n_chase pattern to properly chase through all LED positions
- Replace oscillating behavior with proper chasing movement
- Use pattern_step for internal tracking instead of controller's step
- Calculate position relative to chase head: (i - pattern_step) % num_leds
- Chase head moves through all LED positions with n3 step multiplier
- n1 controls width of lit chase segment
2025-09-19 00:22:25 +12:00
67c4a1a6f6 Update LED bar to handle message type field
- Process 't' field to distinguish between beat ('b') and update ('u') messages
- Beat messages: execute pattern immediately using current parameters
- Update messages: only update parameters, don't execute pattern
- Maintains backward compatibility with default to beat if 't' not specified
- Enables proper synchronization between controller and bars
2025-09-18 22:10:23 +12:00
748ad4b507 Add n3 step rate functionality to patterns 2025-09-18 20:35:21 +12:00
1275d60aaa Make alternating pattern timing independent of n1
- Changed alternating pattern to return delay/2 instead of delay
- Each phase now lasts delay/2, making full cycle equal to delay
- n1 now only controls ON/OFF segment width, not timing
2025-09-18 19:11:35 +12:00
d8e853183b main: enforce event-driven behavior; run selected pattern once per message; clarify comments; fix pattern lookup 2025-09-17 20:20:41 +12:00
8cfb3e156b patterns: add rainbow, specto, and radiate (out then dark-out)
radiate: origins every n1, step by delay, stop when full, dark wave outward, ensure strip off at end, run once

alternating: use n1 as ON width and n2 as OFF width; phase via self.step

pulse: attack (n1), hold (delay), decay (n2); stop at end

tests: add specto sweep (n1_sequence) and radiate demo; include n index per message; use nested {name:{...}} schema; support iterations/repeat-delay
2025-09-16 22:28:51 +12:00
d599af271b patterns: alternating uses n1 (on) and n2 (off); ensure visible ON color; return delay; phase via self.step
test: WS client sends nested {name:{...}}; add iterations and repeat-delay; include n per message; use n1/n2 for alternating
2025-09-16 21:22:47 +12:00
93560a253e patterns: fix blink timing; slow alternating; unify self-test with absolute tick scheduling 2025-09-15 14:12:43 +12:00
d68817ea18 Pipfile.lock: update lockfile 2025-09-15 12:58:51 +12:00
a7a2274a59 Pipfile: sync dependencies 2025-09-15 12:58:45 +12:00
df838dc4d6 settings: adjust defaults and color order handling 2025-09-15 12:58:39 +12:00
4ec48b9f8f main: update loop/test harness configuration 2025-09-15 12:58:30 +12:00
1456ed8a6e boot: minor adjustments 2025-09-15 12:58:20 +12:00
80d5a66fab patterns: centralize timing in tick(); remove selected-delay coupling; update self-test to use per-config durations 2025-09-15 12:56:57 +12:00
64 changed files with 4503 additions and 1737 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,2 @@
settings.json
.venv
__pycache__

71
8_BAR_SETUP.md Normal file
View File

@@ -0,0 +1,71 @@
# 8-LED Bar System Setup
This system supports 8 LED bars working together, each with unique names "100" through "107".
## Quick Setup
### 1. Configure Each LED Bar
Each LED bar needs a unique name. Run the configuration script on each bar:
```bash
python configure_bar.py
```
Then enter the bar name (100, 101, 102, etc.) when prompted.
### 2. Update Bar Names (Optional)
To change the bar names, edit `/home/jimmy/projects/lighting-controller/src/bar_config.py`:
```python
LED_BAR_NAMES = [
"100", # Bar 1
"101", # Bar 2
"102", # Bar 3
"103", # Bar 4
"104", # Bar 5
"105", # Bar 6
"106", # Bar 7
"107", # Bar 8
]
```
### 3. Default Settings
All bars use the same default settings defined in `bar_config.py`:
```python
DEFAULT_BAR_SETTINGS = {
"pattern": "pulse",
"delay": 100,
"colors": [(0, 255, 0)], # Default green
"brightness": 100,
"num_leds": 200,
"n1": 10,
"n2": 10,
"n3": 1,
"n": 0,
}
```
## How It Works
1. **Lighting Controller** sends ESP-NOW messages to all bars simultaneously
2. **Each LED Bar** listens for messages addressed to its unique name
3. **All bars** receive the same pattern/color/brightness settings
4. **Synchronized effects** across all 8 bars
## Current Features
- ✅ All bars show the same pattern simultaneously
- ✅ Individual bar addressing (100-107)
- ✅ Optimized JSON payloads with defaults deduplication
- ✅ Easy configuration via `bar_config.py`
- ✅ MIDI control for all bars
- ✅ n3 step rate functionality
## Future Enhancements
- Sequential patterns (bar 1 → bar 2 → bar 3...)
- Wave effects across bars
- Individual bar control
- Master/slave synchronization
- Physical arrangement awareness

View File

@@ -8,6 +8,7 @@ mpremote = "*"
pyserial = "*"
esptool = "*"
watchfiles = "*"
uvicorn = "*"
[dev-packages]
@@ -15,5 +16,4 @@ watchfiles = "*"
python_version = "3.12"
[scripts]
dev = 'watchfiles "./dev.py /dev/ttyACM0 src reset follow"'
dev = "./dev.py"

546
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "7b8033c15743e27f2589635c75bd0bb86ffc3a725b179d7db9ef200119aa9164"
"sha256": "53809b70ded7a2b3e577a8a4263fbadbb722d1e8d92eb016e134b0776fd40f6b"
},
"pipfile-spec": 6,
"requires": {
@@ -26,142 +26,142 @@
},
"bitarray": {
"hashes": [
"sha256:00628196dd3592972a5183194ab1475dadf9ef2a4cf3fd8c7c184a94934012e8",
"sha256:01d6dc548e7fe5c66913c2274f44855b0f8474935acff7811e84fe1f4024c94f",
"sha256:056fe779f01a867d572e071c0944ac2f3bf58d8bced326040f0bd060af33a209",
"sha256:080a7bf55c432abdae74f25dc3dbff407418346aeae1d43e31f65e8ef114f785",
"sha256:0956322bf4d5e2293e57600aa929c241edf1e209e94e12483bf58c5c691432db",
"sha256:0a6f9e897907757e9c2d722ae6c203d48a04826a14e1495e33935c8583c163a9",
"sha256:0ac446f557eb28e3f7c65372608810ff073840627e9037e22ed10bd081793a34",
"sha256:0b47843f2f288fa746dead4394591a3432a358aaad48240283fa230d6e74b0e7",
"sha256:11fc8bc65f964c7278deb1b7a69379dab3ecc90095f252deb17365637ebb274d",
"sha256:129165b68a3e0c2a633ed0d8557cf5ade24a0b37ca97d7805fa6fc5fb73c19d5",
"sha256:139963494fc3dd5caee5e38c0a03783ef50be118565e94b1dbb0210770f0b32d",
"sha256:157313a124287cbc8a11b55a75def0dd59e68badbc82c2dc2d204dc852742874",
"sha256:16d0edab54bb9d214319418f65bd15cfc4210ec41a16c3dd0b71e626c803212d",
"sha256:1971050b447023288a2b694a03b400bd5163829cd67b10f19e757fe87cd1161e",
"sha256:1c4e75bbf9ade3d2cdf1b607a8b353b17d9b3cf54e88b2a5a773f50ae6f1bfbc",
"sha256:1c9f36055a89b9517db66eb8e80137126bf629c767ceeade4d004e40bc8bcd99",
"sha256:2020102a40edd094c0aa80e09203af71c533c41f76ce3237c99fd194a473ea33",
"sha256:20febc849a1f858e6a57a7d47b323fe9e727c579ddd526d317ad8831748a66a8",
"sha256:220d4b8649ef54ac98e5e0e3dd92230247f67270d1524a8b31aa9859007affb0",
"sha256:22188943a29072b684cd7c99e0b2cfc0af317cea3366c583d820507e6d1f2ed4",
"sha256:222cb27ff05bc0aec72498d075dba1facec49a76a7da45740690cebbe3e81e43",
"sha256:243825f56b58bef28bfc602992a8c6d09bbc625628c195498d6020120d632a09",
"sha256:25060e7162e44242a449ed1a14a4e94b5aef340812754c443459f19c7954be91",
"sha256:26691454a6770628882b68fe74e9f84ca2a51512edd49cbb025b14df5a9dd85a",
"sha256:27d13c7b886afc5d2fc49d6e92f9c96b1f0a14dc7b5502520c29f3da7550d401",
"sha256:27eeee915258b105a21a4b0f8aebc5f77bb4dc4fb4063a09dd329fa1fdcbd234",
"sha256:29ed022189a7997de46cb9bd4e2e49d6163d4f8d78dea72ac5a0e0293b856810",
"sha256:2a324e3007afb5c667026f5235b35efe3c4a95f1b83cd93aa9fce67b42f08e7c",
"sha256:2c533c828d0007fac27cf45e5c1a711e5914dd469db5fe6be5f4e606bf2d7f63",
"sha256:30ba4fba3de1dca653de41c879349ec6ca521d85cff6a7ca5d2fdd8f76c93781",
"sha256:357e07c827bad01f98d0bd0dfdc722f483febeed39140fd75ffd016a451b60b9",
"sha256:3800f3c8c9780f281cf590543fd4b3278fea6988202273a260ecc58136895efb",
"sha256:3d6f3a94abd8b44b2bf346ca81ab2ff41ab9146c53905eedf5178b19d9fe53bf",
"sha256:3eb1390a8b062fe9125e5cc4c5eba990b5d383eec54f2b996e7ce73ac43150f9",
"sha256:407920e9318d94cc6c9611aaa5b5e5963a09f1cbfa17b16b66edea453b3754f4",
"sha256:42622c42c159ea4535bba7e1e3c97f1fec79505bc6873ae657dc0a8f861c60de",
"sha256:4695fcd37478988b1d0a16d5bc0df56dcb677fd5db37f1893d993fd3ebef914b",
"sha256:4798f6744fa2633666e17b4ea8ff70250781b52a25afdbf5ffb5e176c58848f1",
"sha256:4a5b0d277087a5bf261a607fc6ff4aaffcf80b300cd19b7a1e9754a4649f5fd4",
"sha256:4e297fd2e58afe17e33dd80c231c3a9d850279a2a8625aed1d39f9be9534809e",
"sha256:507e567aee4806576e20752f22533e8b7ec61e7e75062a7ce9222a0675aa0da6",
"sha256:50d702149747852923be60cae125285eca8d189d4c7d8832c0c958d4071a0f78",
"sha256:51947a00ae9924584fb14c0c1b1f4c1fd916d9abd6f47582f318ab9c9cb9f3d0",
"sha256:52328192d454ca2ddad09fbc088872b014c74b22ecdd5164717dc7e6442014fa",
"sha256:531e6dfec8058fcf5d69e863b61e6b28e3749b615a4dcc0ab8ad36307c4017fc",
"sha256:54bd71f14a5fa9bae73ef92f2e2be894dc36c7a6d1c4962e5969bd8a9aa39325",
"sha256:552a93be286ca485914777461b384761519db313e0a7f3012dca424c9610a4d5",
"sha256:583b46b3ba44121de5e87e95ae379932dc5fd2e37ebdf2c11a6d7975891425c1",
"sha256:5b58a672ec448fb36839a5fc7bf2b2f60df9a97b872d8bd6ca1a28da6126f5c7",
"sha256:5cfbdccddaa0ff07789e9e180db127906c676e479e05c04830cd458945de3511",
"sha256:5da4939e241301f5e1d18118695e8d2c300be90431b66bd43a00376acec45e1e",
"sha256:5dd9edcab8979a50c2c4dec6d5b66789fb6f630bb52ab90a4548111075a75e48",
"sha256:5e304f94c0353f6ae5711533b5793b3a45b17aa2c5b07e656649b0af4e0939b5",
"sha256:60408ec9c0bd76f1fa00d28034429a0316246d31069b982a86aec8d5c99e910a",
"sha256:62f71b268f14ee6cc3045b95441bfe0518cef1d0b2ffbc6f3e9758f786ff5a03",
"sha256:664d462a4c0783fd755fe3440f07b7e46d149859c96caacadf3f28890f19a8de",
"sha256:66d8b7a89fac6042f7df9ea97d97ed0f5e404281110a882e3babd909161f85b6",
"sha256:6755cfcfa7d8966e704d580c831e39818f85e7b2b7852ad22708973176f0009e",
"sha256:679856547f0b27b98811b73756bdf53769c23b045a6f95177cae634daabf1ddf",
"sha256:6841c08b51417f8ffe398b2828fc0593440c99525c868f640e0302476745320b",
"sha256:68f6e64d4867ee79e25c49d7f35b2b1f04a6d6f778176dcf5b759f3b17a02b2b",
"sha256:69d2d507c1174330c71c834b5d65e66181ad7b42b0d88b5b31804ee9b4f5dae7",
"sha256:6f0be27d06732e2833b672a8fcc32fa195bdb22161eb88f8890de15e30264a01",
"sha256:6f7d2dbe628f3db935622a5b80a5c4d95665cdefc4904372aa3c4d786289477f",
"sha256:72760411d60d8d76979a20ed3f15586d824db04668b581b86e61158c2b616db0",
"sha256:727f7a969416f02ef5c1256541e06f0836fb615022699fa8e2591e85296c5570",
"sha256:77d2368a06a86a18919c05a9b4b0ee9869f770e6a5f414b0fecc911870fe3974",
"sha256:79ab1c5f26f23e51d4a44c4397c8a3bf56c306c125dfab6b3eebdfa13d1dca6f",
"sha256:79db23eda81627132327ed292bd813a9af64399b98aaac3d42ad8deeed24cd5e",
"sha256:7c20d6e6cafce5027e7092beb2ac6eec0d71045d6318b34f36e1387a8c8859a3",
"sha256:7e0851a985a7b10f634188117c825ef99d63402555cca5bc32c7bfc5adaf0d6f",
"sha256:7e2e1ff784c2cdfd863bad31985851427f2d2796e445cec85080c7510cba4315",
"sha256:7f8b12424f8fdf29d1c0749c628bd1530cecfc77374935d096cccc0e4eada232",
"sha256:81e84054b22babcd6c5cc1eac0de2bfc1054ecdf742720cbfb36efbe89ec6c30",
"sha256:84bb57010a1ab76cf880424a2e0bce8dd26989849d2122ff073aa11bfc271c27",
"sha256:870ed23361e2918ab1ffc23fe0ab293abf3c372a68ee7387456d13da3e213008",
"sha256:8cf44b012e7493127ce7ca6e469138ac96b3295a117877d5469aabe7c8728d87",
"sha256:8d6c9bc14bacdfbfd51fed85f0576973eaaa7b30d81ef93264f8e22b86a9c9f7",
"sha256:8d759cecfa8aab4a1eb4e23b6420126b15c7743e85b33f389916bb98c4ecbb84",
"sha256:8ef3f0977c21190f949d5cfd71ded09de87d330c6d98bd5ecb5bb1135d666d0d",
"sha256:8f95daf0ce2b24815ddf62667229ba5dfc0cfee43eb43b2549766170d0f24ae9",
"sha256:911b4a16dce370657e5b8d8b6ba0fbb50dd5e2b24c4416f4b9e664503d3f0502",
"sha256:96117212229905da864794df9ea7bd54987c30a5dcbab3432edc3f344231adae",
"sha256:963cbcf296943f7017470d0b705e63e908f32b4f7dbe43f72c22f6fe1bd9ef66",
"sha256:975a118aa019d745f1398613b27fd8789f60a8cea057a00cdc1abedee123ffe6",
"sha256:9930853d451086c4c084d83a87294bdb0c5bc0fa4105a26c487ac09ea62e565b",
"sha256:99d16862e802e7c50c3b6cdd1bf041b6142335c9c2b426631f731257adfe5a15",
"sha256:9ed4a2852b3de7a64884afcc6936db771707943249a060aec8e551c16361d478",
"sha256:9f7796959c9c036a115d34696563f75d4a2912d3b97c15c15f2a36bdd5496ce9",
"sha256:a04b7a9017b8d0341ebbe77f61b74df1cf1b714f42b671a06f4912dc93d82597",
"sha256:a1b3c4ca3bec8e0ad9d32ce62444c5f3913588124a922629aa7d39357b2adf3f",
"sha256:a290a417608f50137bec731d1f22ff3efebac72845530807a8433b2db9358c95",
"sha256:a33f7c5acf44961f29018b13f0b5f5e1589ac0cfdf75a97c9774cf7ec84d09e0",
"sha256:a39be79a7c36e9a2e20376261c30deb3cdca86b50f7462ae9ff10a755c6720b9",
"sha256:a50a66fa34dd7f9dcdbc7602a1b7bf6f9ab030b4f43e892324193423d9ede180",
"sha256:a5ce1bdee102f7e60c075274df10b892d9ff5183ad6f5f515973eda8903dfe4c",
"sha256:a763dd33d6e27c9b4db3f8089a5fa39179a8a3cf48ce702b24a857d7c621333c",
"sha256:a773199dc42b5d02fcd46c8add552da2c4725ce2caa069527c7e27b5b6089e85",
"sha256:aa3c925502bd0b957a96a5619134bcdc0382ef73cffd40bad218ced3586bcf8d",
"sha256:aeb6db2f4ab54ac21a3851d05130a2aa78a6f6a5f14003f9ae3114fb8b210850",
"sha256:af670708e145b048ead87375b899229443f2d0b4af2d1450d7701c74cd932b03",
"sha256:afa24e5750c9b89ad5a7efef037efe49f4e339f20a94bf678c422c0c71e1207a",
"sha256:b02cc1cac9099c0ec72da09593e7fadb1b6cf88a101acc8153592c700d732d80",
"sha256:b37c9ea942395de029be270f0eca8c141eb14e8455941495cd3b6f95bbe465f4",
"sha256:b3b521e117ab991d6b3b830656f464b354a42dbea2ca16a0e7d93d573f7ab7ff",
"sha256:b5ad8261f47c2a72d0f676bc40f752db8cfdcab911e970753343836e41d5a9a7",
"sha256:b9616ea14917d06736339cf36bb9eaf4eb52110a74136b0dc5eff94e92417d22",
"sha256:b9a03767c937b621ee267507bc394df97fb2f8f61130f39f2033f3c6c191f124",
"sha256:b9ae0008cff25e154ef1e3975a1705d344e844ffdeb34c25b007fd48c876e95d",
"sha256:bdd6412c1f38da7565126b174f4e644f362e317ef0560fac1fb9d0c70900ff4d",
"sha256:bfc417e58f277e949ed662d9cd050ddbb00c0dea8a828abaccc93dc357b7a6d1",
"sha256:c15b9e37bbca59657e4dcc63ad068c821a4676def15f04742c406748a0a11b9c",
"sha256:c677849947d523a082be6e0b5c9137f443a54e951a1711ef003ec52910c41ece",
"sha256:c9d247fcc33c90f2758f4162693250341e3f38cd094f64390076ef33ad0887f9",
"sha256:ca643295bf5441dd38dadf7571ca4b63961820eedbffbe46ceba0893bf226203",
"sha256:ca87f639094c72268e17bc7f57c1225cc38f9e191a489a0134762e3fec402c1a",
"sha256:cc060bc17b9de27874997d612e37d52f72092f9b59d1e04284a90ed8113cadca",
"sha256:ccf4a73e07bfbd790443d6b3c1f1447ffda23cc9391e40c035d9b7d3514b57b8",
"sha256:cf36cadeb9c989f760a13058dbc455e5406ec3d2d247c705c8d4bc6dd1b0fcc6",
"sha256:d47e2bdeba4fb1986af2ba395ce51223f4d460e6e77119439e78f2b592cafade",
"sha256:db78cc5c03b446a43413165aa873e2f408e9fd5ddb45533e7bd3b638bace867c",
"sha256:dbc5029c61f9ebb2d4c247f13584a0ef0e8e49abb13e56460310821aca3ffcaf",
"sha256:ddb319f869d497ef2d3d56319360b61284a9a1d8b3de3bc936748698acfda6be",
"sha256:e0e4fdeae6c0a9d886749780ec5dcf469e98f27b312efa93008d03eaa2426fd5",
"sha256:e4c5e7edf1e7bcbde3b52058f171a411e2a24a081b3e951d685dfea4c3c383d5",
"sha256:e71c9dba78671d38a549e3b2d52514f50e199f9d7e18ed9b0180adeef0d04130",
"sha256:e997d22e0d1e08c8752f61675a75d93659f7aa4dbeaee54207f8d877817b4a0c",
"sha256:efa5834ba5e6c70b22afdca3894097e5a592d8d483c976359654ba990477799a",
"sha256:f2d951002b11962b26afb31f758c18ad39771f287b100fa5adb1d09a47eaaf5b",
"sha256:f3f96f57cea35ba19fd23a20b38fa0dfa3d87d582507129b8c8e314aa298f59b",
"sha256:f738051052abc95dc17f9a4c92044294a263fb7f762efdb13e528d419005c0e4",
"sha256:f76784355060999c36fa807b59faecb38f5769ae58283d00270835773f95e35b",
"sha256:f92462ea3888c99439f58f7561ecd5dd4cf8b8b1b259ccf5376667b8c46ee747",
"sha256:fefd18b29f3b84a0cdea1d86340219d9871c3b0673a38e722a73a2c39591eaa7"
"sha256:002b73bf4a9f7b3ecb02260bd4dd332a6ee4d7f74ee9779a1ef342a36244d0cf",
"sha256:01e3ba46c2dee6d47a4ab22561a01d8ee6772f681defc9fcb357097a055e48cf",
"sha256:03dc877ec286b7f2813185ea6bc5f1f5527fd859e61038d38768883b134e06b3",
"sha256:03eeab48f376c3cd988add2b75c20d2d084b6fcc9a164adb0dc390ef152255b4",
"sha256:05ee46a734b5110c5ac483815da4379f7622f4316362872ec7c0ed16db4b0148",
"sha256:0751596f60f33df66245b2dafa3f7fbe13cb7ac91dd14ead87d8c2eec57cb3ed",
"sha256:08c114cf02a63e13ce6d70bc5b9e7bdcfa8d5db17cece207cfa085c4bc4a7a0c",
"sha256:0ed4a87eda16e2f95d536152c5acccae07841fbdda3b9a752f3dbf43e39f4d6b",
"sha256:101230b8074919970433ef79866570989157ade3421246d4c3afb7a994fdc614",
"sha256:11fcfdf272549a3d876f10d8422bcd5f675750aa746ce04ff04937ec3bb2329e",
"sha256:160f449bb91686f8fc9984200e78b8d793b79e382decf7eb1dc9948d7c21b36f",
"sha256:16426a843b1bc9c552a7c97d6d7555e69730c2de1e2f560503d3fc0e7f6d8005",
"sha256:1f1575cc0f66aa70a0bb5cb57c8d9d1b7d541d920455169c6266919bf804dc20",
"sha256:1f7a8fc5085450635a539c47c9fce6d441b4a973686f88fc220aa20e3921fe55",
"sha256:1fb0a46ae4b8d244a3fb80c3055717baa3dec6be17938e6871042a8d5b4ce670",
"sha256:2965fd8ba31b04c42e4b696fad509dc5ab50663efca6eb06bb3b6d08587f3a09",
"sha256:2b524306104c1296f1e91d74ee4ccbeeea621f6a13e44addf0bb630a1839fd72",
"sha256:2db04b165a57499fbcfe0eaa2f7752f118552bbcfab2163a43fef8d95f4ae745",
"sha256:3092f6bbf4a75b1e6f14a5b1030e27c435f341afeb23987115e45a25cc68ba91",
"sha256:30a2fc37698820cbf9b51d5f801219ef4bed828a04f3307072b8f983dc422a0e",
"sha256:3110b98c5dfb31dc1cf82d8b0c32e3fa6d6d0b268ff9f2a1599165770c1af80f",
"sha256:33f604bffd06b170637f8a48ddcf42074ed1e1980366ac46058e065ce04bfe2a",
"sha256:340c524c7c934b61d1985d805bffe7609180fb5d16ece6ce89b51aa535b936f2",
"sha256:37a6a8382864a1defb5b370b66a635e04358c7334054457bbbb8645610cd95b2",
"sha256:3875578748b484638f6ea776f534e9088cfb15eee131aac051036cba40fd5d05",
"sha256:38b0261483c59bb39ae9300ad46bf0bbf431ab604266382d986a349c96171b36",
"sha256:3b9a2eb7d2e0e9c2f25256d2663c0a2a4798fe3110e3ddbbb1a7b71740b4de08",
"sha256:3bb3cf22c3c03ae698647e6766314149c9cf04aa2018d9f48d5efddc3ced2764",
"sha256:3db0648536f3e08afa7ceb928153c39913f98fd50a5c3adf92a4d0d4268f213e",
"sha256:3dc654da62b3a3027b7c922f7e9f4b27feaabd5d38b2a98ea98de5e8107c72f2",
"sha256:4079857566077f290d35e23ff0e8ba593069c139ae85b0d152b9fa476494f50a",
"sha256:44f468fb4857fff86c65bec5e2fb67067789e40dad69258e9bb78fc6a6df49e7",
"sha256:45660e2fabcdc1bab9699a468b312f47956300d41d6a2ea91c8f067572aaf38a",
"sha256:477b9456eb7d70f385dc8f097a1d66ee40771b62e47b3b3e33406dcfbc1c6a3b",
"sha256:481239cd0966f965c2b8fa78b88614be5f12a64e7773bb5feecc567d39bb2dd5",
"sha256:4a83d247420b147d4b3cba0335e484365e117dc1cfe5ab35acd6a0817ad9244f",
"sha256:53d2abeabb91a822e9d76420c9b44980edd2d6b21767c7bb9cb2b1b4cf091049",
"sha256:55c31bc3d2c9e48741c812ee5ce4607c6f33e33f339831c214d923ffc7777d21",
"sha256:567d6891cb1ddbfd0051fcff3cb1bb86efc82ec818d9c5f98c37d59c1d23cc96",
"sha256:57b9df5d38ab49c13eaa9e0152fdfa8501fc23987f6dcf421b73484bfe573918",
"sha256:59ddb8a9f47ec807009c69e582d0de1c86c005f9f614557f4cebc7b8ac9b7d28",
"sha256:61b9f3cf3a55322baed8f0532b73bce77d688a01446c179392c4056ab74eb551",
"sha256:639389b023315596e0293f85999645f47ec3dc28c892e51242dde6176c91486b",
"sha256:64d1143e90299ba8c967324840912a63a903494b1870a52f6675bda53dc332f7",
"sha256:6542e1cfe060badd160cd383ad93a84871595c14bb05fb8129f963248affd946",
"sha256:69687ef16d501c9217675af36fa3c68c009c03e184b07d22ba245e5c01d47e6b",
"sha256:6f7e1cdf0abb11718e655bb258920453b1e89c2315e9019f60f0775704b12a8c",
"sha256:7378055c9f456c5bb034ac313d9a9028fc6597619a0b16584099adba5a589fdb",
"sha256:78103afbd0a94ac4c1f0b4014545fd149b968d5ea423aaa3b1f6e2c3fc19423e",
"sha256:79038bf1a7b13d243e51f4b6909c6997c2ba2bffc45bcae264704308a2d17198",
"sha256:795b1760418ab750826420ae24f06f392c08e21dc234f0a369a69cc00444f8ec",
"sha256:7998dfb1e9e0255fb8553abb019c3e7f558925de4edc8604243775ff9dd3898d",
"sha256:7afc740ad45ee0e0cef055765faf64789c2c183eb4aa3ecb8cecdb4b607396b3",
"sha256:7b4a41dc183d7d16750634f65566205990f94144755a39f33da44c0350c3e1a8",
"sha256:7f825ebedcad87a2825ddb6cf62f6d7d5b7a56ddaf7c93eef4b974e7ddc16408",
"sha256:7f9f9bb2c5cc1f679605ebbeb72f46fc395d850b93fa7de7addd502a1dc66e99",
"sha256:7fdf059d4e3acec44f512ebe247718ae511fde632e2b06992022df8e637385a6",
"sha256:81e4648c09103bc18f488957c1e0863d2397bab6625c0e6771891f151ee0bd96",
"sha256:8489bff00a1f81ac0754355772e76775878c32a42f16f01d427c3645546761c4",
"sha256:851398428f5604c53371b72c5e0a28163274264ada4a08cd1eafe65fde1f68d0",
"sha256:87a29b8a4cc72af6118954592dcd4e49223420470ccc3f8091c255f6c7330bb1",
"sha256:8b8e07374d60040b24d1a158895d9758424db13be63d4b2fe1870e37f9dec009",
"sha256:8d4aa56782368269eb9402caf7378b2a5ada6f05eb9c7edc2362be258973fd7e",
"sha256:97c448a20aded59727261468873d9b11dfdcce5a6338a359135667d5e3f1d070",
"sha256:98373c273e01a5a7c17103ecb617de7c9980b7608351d58c72198e3525f0002e",
"sha256:98e4a17f55f3cbf6fe06cc79234269572f234467c8355b6758eb252073f78e6b",
"sha256:99124e39658b2f72d296819ec03418609dd4f1b275b00289c2f278a19da6f9c0",
"sha256:9ad0df7886cb9d6d2ff75e87d323108a0e32bdca5c9918071681864129ce8ea8",
"sha256:9bfdfe2e2af434d3f4e47250f693657334e34a7ec557cd703b129a814422b4b8",
"sha256:9faa4c6fcb19a31240ad389426699a99df481b6576f7286471e24efbf1b44dfc",
"sha256:a048e41e1cb0c1a37021269d02698e30d2a7cc9a0205dd3390e0807745b76dae",
"sha256:a05982bb49c73463cb0f0f4bed2d8da82631708a2c2d1926107ba99651b419ec",
"sha256:a23b5f13f9b292004e94b0b13fead4dae79c7512db04dc817ff2c2478298e04a",
"sha256:a393b0f881eff94440f72846a6f0f95b983594a0a50af81c41ed18107420d6a7",
"sha256:a3b6bd81c77d9925809b714980cd30b1831a86bd090316d37cab124d92af1daf",
"sha256:a43f4631ecb87bedc510568fef67db53f2a20c4a5953a9d1e07457e7b1d14911",
"sha256:a569c993942ac26c6c590639ed6712c6c9c3f0c8d287a067bf2a60eb615f3c6b",
"sha256:a5b89349f05431270d1ccc7321aaab91c42ff33f463868779e502438b7f0e668",
"sha256:ac39319e6322c2c093a660c02cea6bb3b1ae53d049b573d4781df8896e443e04",
"sha256:acc56700963f63307ac096689d4547e8061028a66bb78b90e42c5da2898898fb",
"sha256:b723f9d10f7d8259f010b87fa66e924bb4d67927d9dcff4526a755e9ee84fef4",
"sha256:b99a0347bc6131046c19e056a113daa34d7df99f1f45510161bc78bc8461a470",
"sha256:bc0880011b86f81c5353ce4abaeb2472d942ba2320985166a2a3dd4f783563a9",
"sha256:be2f40045432e8aa33d9fd5cb43c91b0c61d77d3d8810f88e84e2e46411c27a7",
"sha256:bebb17125373c499beea009cc5bced757bde52bcb3fa1d6335650e6c2d8111d7",
"sha256:befac6644c6f304a1b6a7948a04095682849c426cebcc44cb2459aa92d3e1735",
"sha256:c1f4880bcb6fb7a8e2ab89128032b3dcf59e1e877ff4493b11c8bf7c3a5b3df2",
"sha256:c3e014f7295b9327fa6f0b3e55a3fd485abac98be145b9597e0cdbb05c44ad07",
"sha256:c427dfcce13a8c814556dfe7c110b8ef61b8fab5fca0d856d4890856807321dc",
"sha256:c44cf0059633470c6bb415091def546adbeb5dcfa91cc3fcb1ac16593f14e52a",
"sha256:c4e04c12f507942f1ddf215cb3a08c244d24051cdd2ba571060166ce8a92be16",
"sha256:c65257899bb8faf6a111297b4ff0066324a6b901318582c0453a01422c3bcd5a",
"sha256:c6c48cf5a92244ef3df4161c8625ee1890bb3d931db9a9f3b699e61a037cd58a",
"sha256:c9bf2bf29854f165a47917b8782b6cf3a7d602971bf454806208d0cbb96f797a",
"sha256:ca4b6298c89b92d6b0a67dfc5f98d68ae92b08101d227263ef2033b9c9a03a72",
"sha256:cc76ad7453816318d794248fba4032967eaffd992d76e5d1af10ef9d46589770",
"sha256:cd7f6bfa2a36fb91b7dec9ddf905716f2ed0c3675d2b63c69b7530c9d211e715",
"sha256:d12c45da97b2f31d0233e15f8d68731cfa86264c9f04b2669b9fdf46aaf68e1f",
"sha256:d160173efdad8a57c22e422a034196df3d84753672c497aee2f94bd5b128f8dd",
"sha256:d2b1ed363a4ef5622dccbf7822f01b51195062c4f382b28c9bd125d046d0324c",
"sha256:d30e7daaf228e3d69cdd8b02c0dd4199cec034c4b93c80109f56f4675a6db957",
"sha256:d3f38373d9b2629dedc559e647010541cc4ec4ad9bea560e2eb1017e6a00d9ef",
"sha256:d7e274ac1975e55ebfb8166cce27e13dc99120c1d6ce9e490d7a716b9be9abb5",
"sha256:d877759842ff9eb16d9c2b8b497953a7d994d4b231c171515f0bf3a2ae185c0c",
"sha256:da3dfd2776226e15d3288a3a24c7975f9ee160ba198f2efa66bc28c5ba76d792",
"sha256:db0441e80773d747a1ed9edfb9f75e7acb68ce8627583bbb6f770b7ec49f0064",
"sha256:dbbaa147cf28b3e87738c624d390a3a9e2a5dfef4316f4c38b4ecaf3155a3eab",
"sha256:ddc646cec4899a137c134b13818469e4178a251d77f9f4b23229267e3da78cfb",
"sha256:df7cc9584614f495f474a5ded365cf72decbcee4efcdc888d2943f8a794c789e",
"sha256:dfde50ae55e075dcd5801e2c3ea0e749c849ed2cbbee991af0f97f1bdbadb2a6",
"sha256:e15e70a3cf5bb519e2448524d689c02ff6bcd4750587a517e2bffee06065bf27",
"sha256:e3572889fcb87e5ca94add412d8b365dbb7b59773a4362e52caa556e5fd98643",
"sha256:e39f5e85e1e3d7d84ac2217cd095b3678306c979e991532df47012880e02215d",
"sha256:e501bd27c795105aaba02b5212ecd1bb552ca2ee2ede53e5a8cb74deee0e2052",
"sha256:e62892645f6a214eefb58a42c3ed2501af2e40a797844e0e09ec1e400ce75f3d",
"sha256:e75eb1734046291c554d9addecca9a8785bdf5d53a64f525569f8549da863dde",
"sha256:e84cff8e8fe71903a6cf873fb3c8731df8bd7c1dac878e7a0fe19d8e2ef39aa9",
"sha256:ea60cf85b4e5a78b5a41eed3a65abc3839a50d915c6e0f6966cbcf81b85991bd",
"sha256:ec3fd30622180cbe2326d48c14a4ab7f98a504b104bdca7dda88b134adad6e31",
"sha256:eccc6829035c8b7b391a0aa124fade54932bb937dd1079f2740b9f1bde829226",
"sha256:eda67136343db96752e58ef36ac37116f36cba40961e79fd0e9bd858f5a09b38",
"sha256:ef5a99a8d1a5c47b4cf85925d1420fc4ee584c98be8efc548651447b3047242f",
"sha256:f0795e2be2aa8afd013635f30ffe599cc00f1bbaca2d1d19b6187b4d1c58fb44",
"sha256:f31d8c2168bf2a52e4539232392352832c2296e07e0e14b6e06a44da574099ba",
"sha256:f41a4b57cbc128a699e9d716a56c90c7fc76554e680fe2962f49cc4d8688b051",
"sha256:f583a1fb180a123c00064fab1a3bfb9d43e574b6474be1be3f6469e0331e3e2e",
"sha256:f7c531722e8c3901f6bb303db464cac98ab44ed422c0fd0c762baa4a8d49ffa1",
"sha256:f8ab90410b2ba5b8276657c66941bcaae556a38be8dd81630a7647e8735f0a20",
"sha256:fa05460dc4f57358680b977b4a254d331b24c8beb501319b998625fd6a22654b",
"sha256:fbe1ef622748d2edb3dd4fef933b934e90e479f9831dfe31bda3fdc16bf5287f",
"sha256:fdb7af369df317527d697c5bb37ab944bb9a17ea1a5e82e47d5c7c638f3ccdd6",
"sha256:fe1f1f4010244cb07f6a079854a12e1627e4fb9ea99d672f2ceccaf6653ca514",
"sha256:fe2493d3f49e314e573022ead4d8c845c9748979b7eb95e815429fe947c4bde2",
"sha256:ffd112646486a31ea5a45aa1eca0e2cd90b6a12f67e848e50349e324c24cc2e7"
],
"version": "==3.6.0"
"version": "==3.7.1"
},
"bitstring": {
"hashes": [
@@ -173,76 +173,93 @@
},
"cffi": {
"hashes": [
"sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8",
"sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2",
"sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1",
"sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15",
"sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36",
"sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824",
"sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8",
"sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36",
"sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17",
"sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf",
"sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc",
"sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3",
"sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed",
"sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702",
"sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1",
"sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8",
"sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903",
"sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6",
"sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d",
"sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b",
"sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e",
"sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be",
"sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c",
"sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683",
"sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9",
"sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c",
"sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8",
"sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1",
"sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4",
"sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655",
"sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67",
"sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595",
"sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0",
"sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65",
"sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41",
"sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6",
"sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401",
"sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6",
"sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3",
"sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16",
"sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93",
"sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e",
"sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4",
"sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964",
"sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c",
"sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576",
"sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0",
"sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3",
"sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662",
"sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3",
"sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff",
"sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5",
"sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd",
"sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f",
"sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5",
"sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14",
"sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d",
"sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9",
"sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7",
"sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382",
"sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a",
"sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e",
"sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a",
"sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4",
"sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99",
"sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
"sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
"sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb",
"sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b",
"sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f",
"sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9",
"sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44",
"sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2",
"sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c",
"sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75",
"sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65",
"sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e",
"sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a",
"sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e",
"sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25",
"sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a",
"sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe",
"sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b",
"sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91",
"sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592",
"sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187",
"sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c",
"sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1",
"sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94",
"sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba",
"sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb",
"sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165",
"sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529",
"sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca",
"sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c",
"sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6",
"sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c",
"sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0",
"sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743",
"sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63",
"sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5",
"sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5",
"sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4",
"sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d",
"sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b",
"sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93",
"sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205",
"sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27",
"sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512",
"sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d",
"sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c",
"sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037",
"sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26",
"sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322",
"sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb",
"sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c",
"sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8",
"sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4",
"sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414",
"sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9",
"sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664",
"sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9",
"sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775",
"sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739",
"sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc",
"sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062",
"sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe",
"sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9",
"sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92",
"sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5",
"sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13",
"sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d",
"sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26",
"sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f",
"sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495",
"sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b",
"sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6",
"sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c",
"sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef",
"sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5",
"sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18",
"sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad",
"sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3",
"sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7",
"sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5",
"sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534",
"sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49",
"sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2",
"sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5",
"sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453",
"sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"
],
"markers": "platform_python_implementation != 'PyPy'",
"version": "==1.17.1"
"version": "==2.0.0"
},
"click": {
"hashes": [
@@ -254,46 +271,46 @@
},
"cryptography": {
"hashes": [
"sha256:00e8724bdad672d75e6f069b27970883179bd472cd24a63f6e620ca7e41cc0c5",
"sha256:048e7ad9e08cf4c0ab07ff7f36cc3115924e22e2266e034450a890d9e312dd74",
"sha256:0d9ef57b6768d9fa58e92f4947cea96ade1233c0e236db22ba44748ffedca394",
"sha256:18f878a34b90d688982e43f4b700408b478102dd58b3e39de21b5ebf6509c301",
"sha256:1b7fa6a1c1188c7ee32e47590d16a5a0646270921f8020efc9a511648e1b2e08",
"sha256:20ae4906a13716139d6d762ceb3e0e7e110f7955f3bc3876e3a07f5daadec5f3",
"sha256:20d15aed3ee522faac1a39fbfdfee25d17b1284bafd808e1640a74846d7c4d1b",
"sha256:2384f2ab18d9be88a6e4f8972923405e2dbb8d3e16c6b43f15ca491d7831bd18",
"sha256:275ba5cc0d9e320cd70f8e7b96d9e59903c815ca579ab96c1e37278d231fc402",
"sha256:2dac5ec199038b8e131365e2324c03d20e97fe214af051d20c49db129844e8b3",
"sha256:31a2b9a10530a1cb04ffd6aa1cd4d3be9ed49f7d77a4dafe198f3b382f41545c",
"sha256:3436128a60a5e5490603ab2adbabc8763613f638513ffa7d311c900a8349a2a0",
"sha256:3b5bf5267e98661b9b888a9250d05b063220dfa917a8203744454573c7eb79db",
"sha256:3de77e4df42ac8d4e4d6cdb342d989803ad37707cf8f3fbf7b088c9cbdd46427",
"sha256:44647c5d796f5fc042bbc6d61307d04bf29bccb74d188f18051b635f20a9c75f",
"sha256:550ae02148206beb722cfe4ef0933f9352bab26b087af00e48fdfb9ade35c5b3",
"sha256:599c8d7df950aa68baa7e98f7b73f4f414c9f02d0e8104a30c0182a07732638b",
"sha256:5b64e668fc3528e77efa51ca70fadcd6610e8ab231e3e06ae2bab3b31c2b8ed9",
"sha256:5bd6020c80c5b2b2242d6c48487d7b85700f5e0038e67b29d706f98440d66eb5",
"sha256:5c966c732cf6e4a276ce83b6e4c729edda2df6929083a952cc7da973c539c719",
"sha256:629127cfdcdc6806dfe234734d7cb8ac54edaf572148274fa377a7d3405b0043",
"sha256:705bb7c7ecc3d79a50f236adda12ca331c8e7ecfbea51edd931ce5a7a7c4f012",
"sha256:780c40fb751c7d2b0c6786ceee6b6f871e86e8718a8ff4bc35073ac353c7cd02",
"sha256:7a3085d1b319d35296176af31c90338eeb2ddac8104661df79f80e1d9787b8b2",
"sha256:826b46dae41a1155a0c0e66fafba43d0ede1dc16570b95e40c4d83bfcf0a451d",
"sha256:833dc32dfc1e39b7376a87b9a6a4288a10aae234631268486558920029b086ec",
"sha256:cc4d66f5dc4dc37b89cfef1bd5044387f7a1f6f0abb490815628501909332d5d",
"sha256:d063341378d7ee9c91f9d23b431a3502fc8bfacd54ef0a27baa72a0843b29159",
"sha256:e2a21a8eda2d86bb604934b6b37691585bd095c1f788530c1fcefc53a82b3453",
"sha256:e40b80ecf35ec265c452eea0ba94c9587ca763e739b8e559c128d23bff7ebbbf",
"sha256:e5b3dda1b00fb41da3af4c5ef3f922a200e33ee5ba0f0bc9ecf0b0c173958385",
"sha256:ea3c42f2016a5bbf71825537c2ad753f2870191134933196bee408aac397b3d9",
"sha256:eccddbd986e43014263eda489abbddfbc287af5cddfd690477993dbb31e31016",
"sha256:ee411a1b977f40bd075392c80c10b58025ee5c6b47a822a33c1198598a7a5f05",
"sha256:f4028f29a9f38a2025abedb2e409973709c660d44319c61762202206ed577c42",
"sha256:f68f833a9d445cc49f01097d95c83a850795921b3f7cc6488731e69bde3288da",
"sha256:fc022c1fa5acff6def2fc6d7819bbbd31ccddfe67d075331a65d9cfb28a20983"
"sha256:06ce84dc14df0bf6ea84666f958e6080cdb6fe1231be2a51f3fc1267d9f3fb34",
"sha256:16ede8a4f7929b4b7ff3642eba2bf79aa1d71f24ab6ee443935c0d269b6bc513",
"sha256:18fcf70f243fe07252dcb1b268a687f2358025ce32f9f88028ca5c364b123ef5",
"sha256:1993a1bb7e4eccfb922b6cd414f072e08ff5816702a0bdb8941c247a6b1b287c",
"sha256:1f3d56f73595376f4244646dd5c5870c14c196949807be39e79e7bd9bac3da63",
"sha256:258e0dff86d1d891169b5af222d362468a9570e2532923088658aa866eb11130",
"sha256:2f641b64acc00811da98df63df7d59fd4706c0df449da71cb7ac39a0732b40ae",
"sha256:3808e6b2e5f0b46d981c24d79648e5c25c35e59902ea4391a0dcb3e667bf7443",
"sha256:3994c809c17fc570c2af12c9b840d7cea85a9fd3e5c0e0491f4fa3c029216d59",
"sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee",
"sha256:465ccac9d70115cd4de7186e60cfe989de73f7bb23e8a7aa45af18f7412e75bf",
"sha256:48c41a44ef8b8c2e80ca4527ee81daa4c527df3ecbc9423c41a420a9559d0e27",
"sha256:4a862753b36620af6fc54209264f92c716367f2f0ff4624952276a6bbd18cbde",
"sha256:4b1654dfc64ea479c242508eb8c724044f1e964a47d1d1cacc5132292d851971",
"sha256:4bd3e5c4b9682bc112d634f2c6ccc6736ed3635fc3319ac2bb11d768cc5a00d8",
"sha256:577470e39e60a6cd7780793202e63536026d9b8641de011ed9d8174da9ca5339",
"sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6",
"sha256:7285a89df4900ed3bfaad5679b1e668cb4b38a8de1ccbfc84b05f34512da0a90",
"sha256:81823935e2f8d476707e85a78a405953a03ef7b7b4f55f93f7c2d9680e5e0691",
"sha256:8978132287a9d3ad6b54fcd1e08548033cc09dc6aacacb6c004c73c3eb5d3ac3",
"sha256:a20e442e917889d1a6b3c570c9e3fa2fdc398c20868abcea268ea33c024c4083",
"sha256:a24ee598d10befaec178efdff6054bc4d7e883f615bfbcd08126a0f4931c83a6",
"sha256:b04f85ac3a90c227b6e5890acb0edbaf3140938dbecf07bff618bf3638578cf1",
"sha256:b6a0e535baec27b528cb07a119f321ac024592388c5681a5ced167ae98e9fff3",
"sha256:bef32a5e327bd8e5af915d3416ffefdbe65ed975b646b3805be81b23580b57b8",
"sha256:bfb4c801f65dd61cedfc61a83732327fafbac55a47282e6f26f073ca7a41c3b2",
"sha256:c13b1e3afd29a5b3b2656257f14669ca8fa8d7956d509926f0b130b600b50ab7",
"sha256:c987dad82e8c65ebc985f5dae5e74a3beda9d0a2a4daf8a1115f3772b59e5141",
"sha256:ce7a453385e4c4693985b4a4a3533e041558851eae061a58a5405363b098fcd3",
"sha256:d0c5c6bac22b177bf8da7435d9d27a6834ee130309749d162b26c3105c0795a9",
"sha256:d97cf502abe2ab9eff8bd5e4aca274da8d06dd3ef08b759a8d6143f4ad65d4b4",
"sha256:dad43797959a74103cb59c5dac71409f9c27d34c8a05921341fb64ea8ccb1dd4",
"sha256:dd342f085542f6eb894ca00ef70236ea46070c8a13824c6bde0dfdcd36065b9b",
"sha256:de58755d723e86175756f463f2f0bddd45cc36fbd62601228a3f8761c9f58252",
"sha256:f3df7b3d0f91b88b2106031fd995802a2e9ae13e02c36c1fc075b43f420f3a17",
"sha256:f5414a788ecc6ee6bc58560e85ca624258a55ca434884445440a810796ea0e0b",
"sha256:fa26fa54c0a9384c27fcdc905a2fb7d60ac6e47d14bc2692145f2b3b1e2cfdbd"
],
"markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'",
"version": "==45.0.6"
"version": "==45.0.7"
},
"esptool": {
"hashes": [
@@ -303,6 +320,14 @@
"markers": "python_version >= '3.10'",
"version": "==5.0.2"
},
"h11": {
"hashes": [
"sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1",
"sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"
],
"markers": "python_version >= '3.8'",
"version": "==0.16.0"
},
"idna": {
"hashes": [
"sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
@@ -320,11 +345,11 @@
},
"markdown-it-py": {
"hashes": [
"sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1",
"sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"
"sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147",
"sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"
],
"markers": "python_version >= '3.8'",
"version": "==3.0.0"
"markers": "python_version >= '3.10'",
"version": "==4.0.0"
},
"mdurl": {
"hashes": [
@@ -345,19 +370,19 @@
},
"platformdirs": {
"hashes": [
"sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc",
"sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"
"sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85",
"sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"
],
"markers": "python_version >= '3.9'",
"version": "==4.3.8"
"version": "==4.4.0"
},
"pycparser": {
"hashes": [
"sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
"sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
"sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2",
"sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"
],
"markers": "python_version >= '3.8'",
"version": "==2.22"
"markers": "implementation_name != 'PyPy'",
"version": "==2.23"
},
"pygments": {
"hashes": [
@@ -467,11 +492,20 @@
},
"typing-extensions": {
"hashes": [
"sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36",
"sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"
"sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466",
"sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"
],
"markers": "python_version < '3.13'",
"version": "==4.15.0"
},
"uvicorn": {
"hashes": [
"sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a",
"sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01"
],
"index": "pypi",
"markers": "python_version >= '3.9'",
"version": "==4.14.1"
"version": "==0.35.0"
},
"watchfiles": {
"hashes": [

58
configure_bar.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
LED Bar Configuration Script
Updates the settings.json file for each LED bar with its unique name
"""
import json
import os
# LED Bar names/IDs
LED_BAR_NAMES = ["100", "101", "102", "103", "104", "105", "106", "107"]
def update_bar_settings(bar_name, settings_file="settings.json"):
"""Update the settings.json file with the bar name"""
if not os.path.exists(settings_file):
print(f"Error: {settings_file} not found")
return False
# Read current settings
with open(settings_file, 'r') as f:
settings = json.load(f)
# Update the name
settings["name"] = bar_name
# Write back to file
with open(settings_file, 'w') as f:
json.dump(settings, f, indent=4)
print(f"Updated {settings_file} with name: {bar_name}")
return True
def main():
print("LED Bar Configuration Script")
print("=" * 40)
print("Available bar names:", LED_BAR_NAMES)
print()
while True:
print("Enter bar name to configure (or 'quit' to exit):")
bar_name = input("> ").strip()
if bar_name.lower() == 'quit':
break
if bar_name not in LED_BAR_NAMES:
print(f"Invalid bar name. Must be one of: {LED_BAR_NAMES}")
continue
if update_bar_settings(bar_name):
print(f"Successfully configured LED bar as '{bar_name}'")
else:
print("Failed to update settings")
print()
if __name__ == "__main__":
main()

165
edit_settings.py Normal file
View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python3
"""
Settings editor - copy from device, edit locally, upload back
Run with: python3 edit_settings.py
"""
import json
import subprocess
import os
import tempfile
def copy_settings_from_device():
"""Copy settings.json from the MicroPython device"""
print("Copying settings from device...")
try:
# Use mpremote to copy settings.json to local temp file
result = subprocess.run(['mpremote', 'cp', ':/settings.json', 'settings.json'],
capture_output=True, text=True)
if result.returncode == 0:
print("Settings copied successfully")
return True
else:
print(f"Failed to copy settings: {result.stderr}")
return False
except Exception as e:
print(f"Error copying settings: {e}")
return False
def load_local_settings():
"""Load settings from local file"""
try:
with open('settings.json', 'r') as f:
return json.load(f)
except FileNotFoundError:
print("No local settings file found")
return {}
except Exception as e:
print(f"Error loading settings: {e}")
return {}
def save_local_settings(settings):
"""Save settings to local file"""
try:
with open('settings.json', 'w') as f:
json.dump(settings, f, indent=2)
print("Settings saved locally")
return True
except Exception as e:
print(f"Error saving settings: {e}")
return False
def upload_settings_to_device():
"""Upload settings.json to the MicroPython device"""
print("Uploading settings to device...")
try:
result = subprocess.run(['mpremote', 'cp', 'settings.json', ':/settings.json'],
capture_output=True, text=True)
if result.returncode == 0:
print("Settings uploaded successfully")
return True
else:
print(f"Failed to upload settings: {result.stderr}")
return False
except Exception as e:
print(f"Error uploading settings: {e}")
return False
def edit_settings_interactive(settings):
"""Interactive settings editor"""
print("\nLED Bar Settings Editor")
print("=======================")
# Display current settings
current_pin = settings.get("led_pin", 10)
current_leds = settings.get("num_leds", 119)
current_name = settings.get("name", "104")
current_color_order = settings.get("color_order", "rgb")
print(f"\nCurrent settings:")
print(f" 1. LED Pin: {current_pin}")
print(f" 2. Number of LEDs: {current_leds}")
print(f" 3. Device Name: {current_name}")
print(f" 4. Color Order: {current_color_order}")
print(f"\nEnter new values (press Enter to keep current):")
# LED Pin
new_pin = input(f"LED Pin [{current_pin}]: ").strip()
if new_pin:
try:
settings["led_pin"] = int(new_pin)
except ValueError:
print("Invalid pin number, keeping current value.")
# Number of LEDs
new_leds = input(f"Number of LEDs [{current_leds}]: ").strip()
if new_leds:
try:
settings["num_leds"] = int(new_leds)
except ValueError:
print("Invalid LED count, keeping current value.")
# Device Name
new_name = input(f"Device Name [{current_name}]: ").strip()
if new_name:
settings["name"] = new_name
# Color Order
print(f"Color Order options: rgb, rbg")
new_color_order = input(f"Color Order [{current_color_order}]: ").strip().lower()
if new_color_order in ['rgb', 'rbg']:
settings["color_order"] = new_color_order
elif new_color_order:
print("Invalid color order, keeping current value.")
return settings
def main():
print("LED Bar Settings Editor")
print("=======================")
# Step 1: Copy settings from device
if not copy_settings_from_device():
print("Creating default settings...")
default_settings = {
"led_pin": 10,
"num_leds": 119,
"color_order": "rgb",
"name": "104"
}
save_local_settings(default_settings)
# Step 2: Load and edit settings
settings = load_local_settings()
if not settings:
print("No settings to edit")
return
# Step 3: Interactive editing
edited_settings = edit_settings_interactive(settings.copy())
# Step 4: Save locally
if edited_settings != settings:
save_local_settings(edited_settings)
# Step 5: Upload to device
upload_choice = input("\nUpload settings to device? (y/n): ").strip().lower()
if upload_choice in ['y', 'yes']:
if upload_settings_to_device():
print("\nSettings updated successfully!")
print("Restart the device to apply changes.")
else:
print("\nFailed to upload settings.")
else:
print("\nSettings saved locally but not uploaded.")
else:
print("\nNo changes made.")
if __name__ == "__main__":
main()

431
patterns.py Normal file
View File

@@ -0,0 +1,431 @@
import utime
import random
from patterns_base import PatternBase # Import PatternBase
class Patterns(PatternBase): # Inherit from PatternBase
def __init__(self, pin, num_leds, color1=(0,0,0), color2=(0,0,0), brightness=127, selected="rainbow_cycle", delay=100):
super().__init__(pin, num_leds, color1, color2, brightness, selected, delay) # Call parent constructor
# Pattern-specific initializations
self.on_width = 1 # Default on width
self.off_width = 2 # Default off width (so total segment is 3, matching original behavior)
self.n1 = 0 # Default start of fill range
self.n2 = self.num_leds - 1 # Default end of fill range
self.oneshot = False # New: One-shot flag for patterns like fill_range
self.patterns = {
"off": self.off,
"on" : self.on,
"color_wipe": self.color_wipe,
"rainbow_cycle": self.rainbow_cycle,
"theater_chase": self.theater_chase,
"blink": self.blink,
"color_transition": self.color_transition, # Added new pattern
"flicker": self.flicker,
"scanner": self.scanner, # New: Single direction scanner
"bidirectional_scanner": self.bidirectional_scanner, # New: Bidirectional scanner
"fill_range": self.fill_range, # New: Fill from n1 to n2
"n_chase": self.n_chase, # New: N1 on, N2 off repeating chase
"alternating": self.alternating, # New: N1 on/off, N2 off/on alternating chase
"external": None,
"pulse": self.pulse
}
# Beat-related functionality removed
# self.selected is already initialized in PatternBase, but we need to ensure it uses our patterns dict
# self.selected = selected # Handled by PatternBase
# Ensure colors list always starts with at least two for robust transition handling
# self.colors handled by PatternBase
# Transition attributes handled by PatternBase
# Scanner attributes handled by PatternBase
# self.run handled by PatternBase
def set_on_width(self, on_width):
self.on_width = on_width
def set_off_width(self, off_width):
self.off_width = off_width
def set_on_off_width(self, on_width, off_width):
self.on_width = on_width
self.off_width = off_width
self.sync()
def set_fill_range(self, n1, n2):
self.n1 = n1
self.n2 = n2
self.sync()
def set_oneshot(self, oneshot_value):
self.oneshot = oneshot_value
if self.oneshot: # Reset pattern step if enabling one-shot
self.pattern_step = 0
self.sync()
def select(self, pattern):
if pattern in self.patterns:
super().select(pattern) # Use parent select to set self.selected and self.transition_step
self.run = True # Set run flag
if pattern == "color_transition":
if len(self.colors) < 2:
print("Warning: 'color_transition' requires at least two colors. Switching to 'on'.")
self.selected = "on" # Fallback if not enough colors
self.sync() # Re-sync for the new pattern
else:
self.transition_step = 0
self.current_color_idx = 0 # Start from the first color in the list
self.current_color = self.colors[self.current_color_idx]
self.hold_start_time = utime.ticks_ms() # Reset hold timer
self.transition_duration = self.delay * 50 # Initialize transition duration
self.hold_duration = self.delay * 10 # Initialize hold duration
return True
return False
def off(self):
self.fill((0, 0, 0))
return self.delay
def on(self):
self.fill(self.apply_brightness(self.colors[0]))
return self.delay
def color_wipe(self):
color = self.apply_brightness(self.colors[0])
current_time = utime.ticks_ms()
if self.pattern_step < self.num_leds:
for i in range(self.num_leds):
self.n[i] = (0, 0, 0)
self.n[self.pattern_step] = self.apply_brightness(color)
self.n.write()
self.pattern_step += 1
else:
self.pattern_step = 0
self.last_update = current_time
return self.delay
def rainbow_cycle(self):
current_time = utime.ticks_ms()
def wheel(pos):
if pos < 85:
return (pos * 3, 255 - pos * 3, 0)
elif pos < 170:
pos -= 85
return (255 - pos * 3, 0, pos * 3)
else:
pos -= 170
return (0, pos * 3, 255 - pos * 3)
# Rainbow travels along the length - each LED position gets a different color
for i in range(self.num_leds):
# Map LED position along strip to rainbow color
# Full 256 color range distributed along the strip length
rc_index = ((i * 256 // self.num_leds) + self.pattern_step) % 256
self.n[i] = self.apply_brightness(wheel(rc_index & 255))
self.n.write()
self.pattern_step = (self.pattern_step + 1) % 256
self.last_update = current_time
return max(1, int(self.delay // 5))
def theater_chase(self):
current_time = utime.ticks_ms()
segment_length = self.on_width + self.off_width
for i in range(self.num_leds):
if (i + self.pattern_step) % segment_length < self.on_width:
self.n[i] = self.apply_brightness(self.colors[0])
else:
self.n[i] = (0, 0, 0)
self.n.write()
self.pattern_step = (self.pattern_step + 1) % segment_length
self.last_update = current_time
return self.delay
def blink(self):
current_time = utime.ticks_ms()
if self.pattern_step % 2 == 0:
self.fill(self.apply_brightness(self.colors[0]))
else:
self.fill((0, 0, 0))
self.pattern_step = (self.pattern_step + 1) % 2
self.last_update = current_time
return self.delay
def color_transition(self):
current_time = utime.ticks_ms()
# Check for hold duration first
if utime.ticks_diff(current_time, self.hold_start_time) < self.hold_duration:
# Still in hold phase, just display the current solid color
self.fill(self.apply_brightness(self.current_color))
self.last_update = current_time # Keep updating last_update to avoid skipping frames
return self.delay
# If hold duration is over, proceed with transition
if utime.ticks_diff(current_time, self.last_update) >= self.delay:
num_colors = len(self.colors)
if num_colors < 2:
# Should not happen if select handles it, but as a safeguard
self.select("on")
return self.delay
from_color = self.colors[self.current_color_idx]
to_color_idx = (self.current_color_idx + 1) % num_colors
to_color = self.colors[to_color_idx]
# Calculate interpolation factor (0.0 to 1.0)
# transition_step goes from 0 to transition_duration - 1
if self.transition_duration > 0:
interp_factor = self.transition_step / self.transition_duration
else:
interp_factor = 1.0 # Immediately transition if duration is zero
# Interpolate each color component
r = int(from_color[0] + (to_color[0] - from_color[0]) * interp_factor)
g = int(from_color[1] + (to_color[1] - from_color[1]) * interp_factor)
b = int(from_color[2] + (to_color[2] - from_color[2]) * interp_factor)
self.current_color = (r, g, b)
self.fill(self.apply_brightness(self.current_color))
self.transition_step += self.delay # Advance the transition step by the delay
if self.transition_step >= self.transition_duration:
# Transition complete, move to the next color and reset for hold phase
self.current_color_idx = to_color_idx
self.current_color = self.colors[self.current_color_idx] # Ensure current_color is the exact target color
self.transition_step = 0 # Reset transition progress
self.hold_start_time = current_time # Start hold phase for the new color
self.last_update = current_time
return self.delay
def flicker(self):
current_time = utime.ticks_ms()
base_color = self.colors[0]
# Increase the range for flicker_brightness_offset
# Changed from self.brightness // 4 to self.brightness // 2 (or even self.brightness for max intensity)
flicker_brightness_offset = random.randint(-int(self.brightness // 1.5), int(self.brightness // 1.5))
flicker_brightness = max(0, min(255, self.brightness + flicker_brightness_offset))
flicker_color = self.apply_brightness(base_color, brightness_override=flicker_brightness)
self.fill(flicker_color)
self.last_update = current_time
return max(1, int(self.delay // 5))
def scanner(self):
"""
Mimics a 'Knight Rider' style scanner, moving in one direction.
"""
current_time = utime.ticks_ms()
self.fill((0, 0, 0)) # Clear all LEDs
# Calculate the head and tail position
head_pos = self.pattern_step
color = self.apply_brightness(self.colors[0])
# Draw the head
if 0 <= head_pos < self.num_leds:
self.n[head_pos] = color
# Draw the trailing pixels with decreasing brightness
for i in range(1, self.scanner_tail_length + 1):
tail_pos = head_pos - i
if 0 <= tail_pos < self.num_leds:
# Calculate fading color for tail
# Example: linear fade from full brightness to off
fade_factor = 1.0 - (i / (self.scanner_tail_length + 1))
faded_color = tuple(int(c * fade_factor) for c in color)
self.n[tail_pos] = faded_color
self.n.write()
self.pattern_step += 1
if self.pattern_step >= self.num_leds + self.scanner_tail_length:
self.pattern_step = 0 # Reset to start
self.last_update = current_time
return self.delay
def bidirectional_scanner(self):
"""
Mimics a 'Knight Rider' style scanner, moving back and forth.
"""
current_time = utime.ticks_ms()
self.fill((0, 0, 0)) # Clear all LEDs
color = self.apply_brightness(self.colors[0])
# Calculate the head position based on direction
head_pos = self.pattern_step
# Draw the head
if 0 <= head_pos < self.num_leds:
self.n[head_pos] = color
# Draw the trailing pixels with decreasing brightness
for i in range(1, self.scanner_tail_length + 1):
tail_pos = head_pos - (i * self.scanner_direction)
if 0 <= tail_pos < self.num_leds:
fade_factor = 1.0 - (i / (self.scanner_tail_length + 1))
faded_color = tuple(int(c * fade_factor) for c in color)
self.n[tail_pos] = faded_color
self.n.write()
self.pattern_step += self.scanner_direction
# Change direction if boundaries are reached
if self.scanner_direction == 1 and self.pattern_step >= self.num_leds:
self.scanner_direction = -1
self.pattern_step = self.num_leds - 1 # Start moving back from the last LED
elif self.scanner_direction == -1 and self.pattern_step < 0:
self.scanner_direction = 1
self.pattern_step = 0 # Start moving forward from the first LED
self.last_update = current_time
return self.delay
def fill_range(self):
"""
Fills a range of LEDs from n1 to n2 with a solid color.
If self.oneshot is True, it fills once and then turns off the LEDs.
"""
current_time = utime.ticks_ms()
if self.oneshot and self.pattern_step >= 1:
self.fill((0, 0, 0)) # Turn off LEDs if one-shot already happened
else:
color = self.apply_brightness(self.colors[0])
for i in range(self.n1, self.n2 + 1):
self.n[i] = color
self.n.write()
self.last_update = current_time
return self.delay
self.last_update = current_time
return self.delay
def n_chase(self):
"""
A theater chase pattern using n1 for on-width and n2 for off-width.
"""
current_time = utime.ticks_ms()
segment_length = self.n1 + self.n2
if segment_length == 0: # Avoid division by zero
self.fill((0,0,0))
self.n.write()
self.last_update = current_time
return self.delay
for i in range(self.num_leds):
if (i + self.pattern_step) % segment_length < self.n1:
self.n[i] = self.apply_brightness(self.colors[0])
else:
self.n[i] = (0, 0, 0)
self.n.write()
self.pattern_step = (self.pattern_step + 1) % segment_length
self.last_update = current_time
return self.delay
def alternating(self):
"""
An alternating pattern where n1 LEDs are ON/OFF and n2 LEDs are OFF/ON globally, without moving.
"""
current_time = utime.ticks_ms()
total_segment_length = self.n1 + self.n2
if total_segment_length == 0:
self.fill((0,0,0))
self.n.write()
self.last_update = current_time
return self.delay
# current_phase will alternate between 0 and 1
current_phase = self.pattern_step % 2
for i in range(self.num_leds):
# Position within a single repeating segment (n1 + n2)
pos_in_segment = i % total_segment_length
if current_phase == 0: # State 0: n1 ON, n2 OFF
if pos_in_segment < self.n1:
self.n[i] = self.apply_brightness(self.colors[0]) # n1 is ON
else:
self.n[i] = (0, 0, 0) # n2 is OFF
else: # State 1: n1 OFF, n2 ON
if pos_in_segment < self.n1:
self.n[i] = (0, 0, 0) # n1 is OFF
else:
self.n[i] = self.apply_brightness(self.colors[0]) # n2 is ON
self.n.write()
self.pattern_step = (self.pattern_step + 1) % 2 # Toggle between 0 and 1
self.last_update = current_time
return self.delay * 2
def pulse(self):
if self.pattern_step == 0:
self.fill(self.apply_brightness(self.colors[0]))
self.pattern_step = 1
self.last_update = utime.ticks_ms()
if utime.ticks_diff(utime.ticks_ms(), self.last_update) > self.delay:
self.fill((0, 0, 0))
print(utime.ticks_diff(utime.ticks_ms(), self.last_update))
self.run = False
return self.delay
if __name__ == "__main__":
import time
from machine import WDT
wdt = WDT(timeout=2000) # Enable watchdog with a 2 second timeout
p = Patterns(pin=4, num_leds=60, color1=(255,0,0), color2=(0,0,255), brightness=127, selected="off", delay=100)
print(p.colors, p.brightness)
tests = [
("off", {"duration_ms": 500}),
("on", {"duration_ms": 500}),
("color_wipe", {"delay": 200, "duration_ms": 1000}),
("rainbow_cycle", {"delay": 100, "duration_ms": 2500}),
("theater_chase", {"on_width": 3, "off_width": 3, "delay": 1000, "duration_ms": 2500}),
("blink", {"delay": 500, "duration_ms": 2000}),
("color_transition", {"delay": 150, "colors": [(255,0,0),(0,255,0),(0,0,255)], "duration_ms": 5000}),
("flicker", {"delay": 100, "duration_ms": 2000}),
("scanner", {"delay": 150, "duration_ms": 2500}),
("bidirectional_scanner", {"delay": 50, "duration_ms": 2500}),
("fill_range", {"n1": 10, "n2": 20, "delay": 500, "duration_ms": 2000}),
("n_chase", {"n1": 5, "n2": 5, "delay": 2000, "duration_ms": 2500}),
("alternating", {"n1": 5, "n2": 5, "delay": 500, "duration_ms": 2500}),
("pulse", {"delay": 100, "duration_ms": 700}),
]
print("\n--- Running pattern self-test ---")
for name, cfg in tests:
print(f"\nPattern: {name}")
# apply simple config helpers
if "delay" in cfg:
p.set_delay(cfg["delay"])
if "on_width" in cfg:
p.set_on_width(cfg["on_width"])
if "off_width" in cfg:
p.set_off_width(cfg["off_width"])
if "n1" in cfg and "n2" in cfg:
p.set_fill_range(cfg["n1"], cfg["n2"])
if "colors" in cfg:
p.set_colors(cfg["colors"])
p.select(name)
# run per configured duration using absolute-scheduled tick(next_due_ms)
start = utime.ticks_ms()
duration_ms = cfg["duration_ms"]
delay = cfg.get("delay", 0)
next_due = utime.ticks_ms() - 1 # force immediate first call
while utime.ticks_diff(utime.ticks_ms(), start) < duration_ms:
delay = p.tick(delay)
wdt.feed()
print("\n--- Test routine finished ---")

View File

@@ -6,4 +6,4 @@ s = Settings()
name = s.get('name', 'led')
password = s.get("ap_password", "")
wifi.ap(name, password)
# wifi.ap(name, password)

View File

@@ -1,5 +1,5 @@
import asyncio
import aioespnow
import patterns
from settings import Settings
from web import web
from patterns import Patterns
@@ -10,38 +10,76 @@ import time
import wifi
import json
from p2p import p2p
import espnow
import network
async def main():
def main():
settings = Settings()
patterns = Patterns(settings["led_pin"], settings["num_leds"], selected=settings["pattern"])
if settings["color_order"] == "rbg": color_order = (1, 5, 3)
else: color_order = (1, 3, 5)
patterns.colors = [(8,0,0)]
async def system():
while True:
gc.collect()
for i in range(60):
wdt.feed()
await asyncio.sleep(1)
w = web(settings, patterns)
print(settings)
# start the server in a bacakground task
print("Starting")
server = asyncio.create_task(w.start_server(host="0.0.0.0", port=80))
patterns = Patterns(settings["led_pin"], settings["num_leds"], selected="off")
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
e = espnow.ESPNow()
e.config(rxbuf=1024)
e.active(True)
# Increase buffer size for 8-bar payloads (default 526 bytes might be too small) # Set to 1KB to handle larger multi-bar payloads
wdt = machine.WDT(timeout=10000)
wdt.feed()
while True:
# advance pattern based on its own returned schedule
# due = patterns.tick(due)
wdt.feed()
asyncio.create_task(p2p(settings, patterns))
asyncio.create_task(system())
patterns.select(settings["pattern"])
await patterns.run()
# Drain all pending packets and only process the latest
last_msg = None
while True:
host, msg = e.recv(0)
if not msg:
break
last_msg = msg
if last_msg:
try:
data = json.loads(last_msg)
print(data)
defaults = data.get("d", {})
bar = data.get(settings.get("name"), {})
# Check message type
message_type = defaults.get("t", "b") # Default to beat if not specified
# Always update parameters from message
param_mapping = {
"br": "brightness",
"dl": "delay",
"cl": "colors",
"n1": "n1",
"n2": "n2",
"n3": "n3",
"n4": "n4",
"s": "step"
}
# Iterate through values in bar and update parameters
print("Bar values:")
for key, value in bar.items():
print(f" {key}: {value}")
# Update parameter if it exists in param_mapping
if key in param_mapping:
attr_name = param_mapping[key]
current_value = getattr(patterns, attr_name)
new_value = bar.get(key, defaults.get(key, current_value))
setattr(patterns, attr_name, new_value)
# Print received parameters
except Exception as ex:
print(f"Failed to load espnow data {last_msg}: {ex}")
continue
# cleanup before ending the application
await server
asyncio.run(main())
main()

View File

@@ -1,16 +0,0 @@
import asyncio
import aioespnow
import json
async def p2p(settings, patterns):
e = aioespnow.AIOESPNow() # Returns AIOESPNow enhanced with async support
e.active(True)
async for mac, msg in e:
try:
data = json.loads(msg)
except:
print(f"Failed to load espnow data {msg}")
continue
if "names" not in data or settings.get("name") in data.get("names", []):
await settings.set_settings(data.get("settings", {}), patterns, data.get("save", False))

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,9 @@
from machine import Pin
from neopixel import NeoPixel
import utime
import random
import _thread
import asyncio
import json
from presets import Presets
# Short-key parameter mapping for convenience setters
param_mapping = {
"pt": "selected",
"pa": "selected",
"cl": "colors",
"br": "brightness",
"dl": "delay",
"nl": "num_leds",
"co": "color_order",
"lp": "led_pin",
"n1": "n1",
"n2": "n2",
"n3": "n3",
"n4": "n4",
"n5": "n5",
"n6": "n6",
"auto": "auto",
}
class Patterns:
class PatternBase:
def __init__(self, pin, num_leds, color1=(0,0,0), color2=(0,0,0), brightness=127, selected="rainbow_cycle", delay=100):
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
self.num_leds = num_leds
@@ -34,9 +11,9 @@ class Patterns:
self.last_update = utime.ticks_ms()
self.delay = delay
self.brightness = brightness
self.auto = False
self.patterns = {}
self.selected = selected
self.run = True
# Ensure colors list always starts with at least two for robust transition handling
self.colors = [color1, color2] if color1 != color2 else [color1, (255, 255, 255)] # Fallback if initial colors are same
if not self.colors: # Ensure at least one color exists
@@ -50,71 +27,25 @@ class Patterns:
self.hold_start_time = utime.ticks_ms() # Time when the current color hold started
# New attributes for scanner patterns
# New attributes for scanner patterns (moved from Patterns to PatternBase as they are generic enough)
self.scanner_direction = 1 # 1 for forward, -1 for backward
self.scanner_tail_length = 3 # Number of trailing pixels
self.running = False
self.stopped = True
self.presets = Presets()
self.n1 = 0
self.n2 = 0
self.n3 = 0
self.n4 = 0
self.n5 = 0
self.n6 = 0
def select(self, pattern):
if pattern in self.patterns:
self.selected = pattern
return True
return False
async def run(self):
print(f"Stopping pattern")
await self.stop()
self.running = True
print(f"Starting pattern {self.selected}")
if self.selected in self.patterns:
_thread.start_new_thread(self.patterns[self.selected], ())
else:
print(f"Pattern {self.selected} not found")
async def stop(self):
self.running = False
start = utime.ticks_ms()
while not self.stopped and utime.ticks_diff(utime.ticks_ms(), start) < 1000:
await asyncio.sleep_ms(0)
self.stopped = True
def set_param(self, key, value):
if key in param_mapping:
setattr(self, param_mapping[key], value)
return True
print(f"Invalid parameter: {key}")
return False
# Store last pattern-returned delay to use for subsequent gating
self._last_returned_delay = None
def update_num_leds(self, pin, num_leds):
self.n = NeoPixel(Pin(pin, Pin.OUT), num_leds)
self.num_leds = num_leds
self.pattern_step = 0
def set_color(self, num, color):
# Changed: More robust index check
if 0 <= num < len(self.colors):
self.colors[num] = color
# If the changed color is part of the current or next transition,
# restart the transition for smoother updates
return True
elif num == len(self.colors): # Allow setting a new color at the end
self.colors.append(color)
return True
return False
def del_color(self, num):
# Changed: More robust index check and using del for lists
if 0 <= num < len(self.colors):
del self.colors[num]
return True
@@ -124,29 +55,16 @@ class Patterns:
effective_brightness = brightness_override if brightness_override is not None else self.brightness
return tuple(int(c * effective_brightness / 255) for c in color)
def write(self):
self.n.write()
def fill(self, color=None):
fill_color = color if color is not None else self.colors[0]
for i in range(self.num_leds):
self.n[i] = fill_color
self.n.fill(fill_color)
self.n.write()
def off(self):
self.fill((0, 0, 0))
def on(self):
self.fill(self.apply_brightness(self.colors[0]))
def wheel(self, pos):
if pos < 85:
return (pos * 3, 255 - pos * 3, 0)
elif pos < 170:
pos -= 85
return (255 - pos * 3, 0, pos * 3)
else:
pos -= 170
return (0, pos * 3, 255 - pos * 3)
self.fill(self.apply_brightness(self.colors[0]))

View File

@@ -9,21 +9,14 @@ class Settings(dict):
def __init__(self):
super().__init__()
self.load() # Load settings from file during initialization
if self["color_order"] == "rbg": self.color_order = (1, 5, 3)
if self.get("color_order", "rgb") == "rbg": self.color_order = (1, 5, 3)
else: self.color_order = (1, 3, 5)
def set_defaults(self):
self["led_pin"] = 10
self["num_leds"] = 50
self["pattern"] = "on"
self["color1"] = "#00ff00"
self["color2"] = "#ff0000"
self["delay"] = 100
self["brightness"] = 10
self["num_leds"] = 119
self["color_order"] = "rgb"
self["name"] = f"led-{ubinascii.hexlify(wifi.get_mac()).decode()}"
self["ap_password"] = ""
self["id"] = 0
self["name"] = f"104"
def save(self):
try:
@@ -45,40 +38,48 @@ class Settings(dict):
self.set_defaults()
self.save()
async def set_settings(self, data, patterns, save):
def set_settings(self, data, patterns, save):
try:
print(f"Setting settings: {data}")
for key, value in data.items():
print(key, value)
if key == "colors":
buff = []
for color in value:
buff.append(tuple(int(color[i:i+2], 16) for i in self.color_order))
patterns.colors = buff
patterns.set_colors(buff)
elif key == "color1":
patterns.set_color1(tuple(int(value[i:i+2], 16) for i in self.color_order)) # Convert hex to RGB
elif key == "color2":
patterns.set_color2(tuple(int(value[i:i+2], 16) for i in self.color_order)) # Convert hex to RGB
elif key == "num_leds":
patterns.update_num_leds(self["led_pin"], value)
elif key == "pattern":
if not patterns.select(value):
return "Pattern doesn't exist", 400
await patterns.run()
elif key == "delay":
delay = int(data["delay"])
patterns.delay = delay
patterns.set_delay(delay)
elif key == "brightness":
brightness = int(data["brightness"])
patterns.brightness = brightness
patterns.set_brightness(brightness)
elif key == "on_width":
on_width = int(data["on_width"])
patterns.set_on_width(on_width)
elif key == "off_width":
off_width = int(data["off_width"])
on_width = int(data.get("on_width", self["on_width"]))
patterns.set_on_off_width(on_width, off_width)
elif key == "n1":
patterns.n1 = value
n1 = int(data["n1"])
n2 = int(data.get("n2", patterns.n2))
patterns.set_fill_range(n1, n2)
elif key == "n2":
patterns.n2 = value
elif key == "n3":
patterns.n3 = value
elif key == "n4":
patterns.n4 = value
elif key == "n5":
patterns.n5 = value
elif key == "n6":
patterns.n6 = value
n2 = int(data["n2"])
n1 = int(data.get("n1", patterns.n1))
patterns.set_fill_range(n1, n2)
elif key == "oneshot":
oneshot_value = bool(data["oneshot"])
patterns.set_oneshot(oneshot_value)
elif key == "name":
self[key] = value
self.save()
@@ -95,11 +96,12 @@ class Settings(dict):
return "Invalid key", 400
self[key] = value
#print(self)
patterns.sync()
if save:
self.save()
print(self)
return "OK", 200
except (KeyError, ValueError):
except Exception as e:
print(f"An unexpected error occurred in set_settings: {e}")
return "Bad request", 400
# Example usage

View File

@@ -12,7 +12,7 @@ def web(settings, patterns):
@app.route('/')
async def index_hnadler(request):
mac = wifi.get_mac().hex()
return Template('index.html').render(settings=settings, patterns=patterns.patterns.keys())
return Template('/index.html').render(settings=settings, patterns=patterns.patterns.keys(), mac=mac)
@app.route("/static/<path:path>")
def static_handler(request, path):
@@ -35,7 +35,7 @@ def web(settings, patterns):
if data:
# Process the received data
_, status_code = await settings.set_settings(json.loads(data), patterns, True)
_, status_code = settings.set_settings(json.loads(data), patterns, True)
#await ws.send(status_code)
else:
break

58
test/blink.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Test script for blink pattern
Run with: mpremote run test/blink.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
def test_blink():
print("Testing blink pattern...")
# Initialize watchdog timer
wdt = WDT(timeout=10000) # 10 second timeout
wdt.feed()
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"], # Set to 200 LEDs for test
brightness=255, # Full brightness
delay=500
)
# Set a bright red color
p.colors = [(255, 0, 0)] # Red
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {p.num_leds} (test override: 200)")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Color: {p.colors[0]}")
# Test blink pattern
print("Starting blink pattern for 5 seconds...")
p.select("bl")
# Let it run for 5 seconds with WDT feeding
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < 5000:
wdt.feed()
utime.sleep_ms(100)
# Stop the pattern
p.run = False
print("Blink test completed")
# Turn off LEDs
p.off()
print("LEDs turned off")
if __name__ == "__main__":
test_blink()

58
test/circle/1.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Circle loading test 1: n1=50, n2=100, n3=200, n4=0 (Red)
Runs forever
Run with: mpremote run test/circle/1.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 1: n1=50, n2=100, n3=200, n4=0 (Red)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 50 # Head moves 50 LEDs/second
p.n2 = 100 # Max length 100 LEDs
p.n3 = 200 # Tail moves 200 LEDs/second
p.n4 = 0 # Min length 0 LEDs
p.colors = [(255, 0, 0)] # Red
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/circle/2.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Circle loading test 2: n1=10, n2=30, n3=20, n4=5 (Green)
Runs forever
Run with: mpremote run test/circle/2.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 2: n1=10, n2=30, n3=20, n4=5 (Green)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 10 # Head moves 10 LEDs/second
p.n2 = 30 # Max length 30 LEDs
p.n3 = 20 # Tail moves 20 LEDs/second
p.n4 = 5 # Min length 5 LEDs
p.colors = [(0, 255, 0)] # Green
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/circle/3.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Circle loading test 3: n1=15, n2=25, n3=15, n4=3 (Blue)
Runs forever
Run with: mpremote run test/circle/3.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 3: n1=15, n2=25, n3=15, n4=3 (Blue)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 15 # Head moves 15 LEDs/second
p.n2 = 25 # Max length 25 LEDs
p.n3 = 15 # Tail moves 15 LEDs/second
p.n4 = 3 # Min length 3 LEDs
p.colors = [(0, 0, 255)] # Blue
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/circle/4.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Circle loading test 4: n1=30, n2=40, n3=5, n4=8 (Yellow)
Runs forever
Run with: mpremote run test/circle/4.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 4: n1=30, n2=40, n3=5, n4=8 (Yellow)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 30 # Head moves 30 LEDs/second
p.n2 = 40 # Max length 40 LEDs
p.n3 = 5 # Tail moves 5 LEDs/second
p.n4 = 8 # Min length 8 LEDs
p.colors = [(255, 255, 0)] # Yellow
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/circle/5.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Circle loading test 5: n1=25, n2=60, n3=30, n4=2 (Magenta)
Runs forever
Run with: mpremote run test/circle/5.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 5: n1=25, n2=60, n3=30, n4=2 (Magenta)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 25 # Head moves 25 LEDs/second
p.n2 = 60 # Max length 60 LEDs
p.n3 = 30 # Tail moves 30 LEDs/second
p.n4 = 2 # Min length 2 LEDs
p.colors = [(255, 0, 255)] # Magenta
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/circle/6.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Circle loading test 6: n1=5, n2=20, n3=40, n4=1 (Cyan)
Runs forever
Run with: mpremote run test/circle/6.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 6: n1=5, n2=20, n3=40, n4=1 (Cyan)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 5 # Head moves 5 LEDs/second
p.n2 = 20 # Max length 20 LEDs
p.n3 = 40 # Tail moves 40 LEDs/second
p.n4 = 1 # Min length 1 LED
p.colors = [(0, 255, 255)] # Cyan
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/circle/7.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Circle loading test 7: n1=12, n2=15, n3=18, n4=4 (Orange)
Runs forever
Run with: mpremote run test/circle/7.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 7: n1=12, n2=15, n3=18, n4=4 (Orange)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 12 # Head moves 12 LEDs/second
p.n2 = 15 # Max length 15 LEDs
p.n3 = 18 # Tail moves 18 LEDs/second
p.n4 = 4 # Min length 4 LEDs
p.colors = [(255, 128, 0)] # Orange
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/circle/8.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
Circle loading test 8: n1=100, n2=50, n3=150, n4=10 (Purple)
Runs forever
Run with: mpremote run test/circle/8.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Circle Loading Test 8: n1=100, n2=50, n3=150, n4=10 (Purple)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000
)
# Configure test parameters
p.n1 = 100 # Head moves 100 LEDs/second
p.n2 = 50 # Max length 50 LEDs
p.n3 = 150 # Tail moves 150 LEDs/second
p.n4 = 10 # Min length 10 LEDs
p.colors = [(128, 0, 255)] # Purple
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Parameters: n1={p.n1}, n2={p.n2}, n3={p.n3}, n4={p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

128
test/circle_loading.py Normal file
View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python3
"""
Test script for circle loading pattern with multiple test cases
Run with: mpremote run test/circle_loading.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
def run_test_case(p, test_name, duration_ms=5000):
"""Run a test case for specified duration"""
print(f"\n--- {test_name} ---")
print(f"Parameters: n1={p.n1} head/sec, n2={p.n2} max length, n3={p.n3} tail/sec, n4={p.n4} min length")
print(f"Color: {p.colors[0]}")
print(f"Running for {duration_ms//1000} seconds...")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("cl")
# Run for specified duration
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < duration_ms:
wdt.feed()
utime.sleep_ms(100)
# Stop pattern
p.run = False
print(f"{test_name} completed")
# Brief pause between tests
utime.sleep_ms(500)
def test_circle_loading():
print("Testing circle loading pattern with multiple configurations...")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000 # Base cycle time
)
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
# Test Case 1: Your specified parameters
p.n1 = 50
p.n2 = 100
p.n3 = 200
p.n4 = 0
p.colors = [(255, 0, 0)] # Red
run_test_case(p, "Test 1: n1=50, n2=100, n3=200, n4=0", 15000)
# Test Case 2: Slow head, fast tail
p.n1 = 10 # Head moves 10 LEDs/second
p.n2 = 30 # Max length 30 LEDs
p.n3 = 20 # Tail moves 20 LEDs/second
p.n4 = 5 # Min length 5 LEDs
p.colors = [(0, 255, 0)] # Green
run_test_case(p, "Test 2: n1=10, n2=30, n3=20, n4=5", 10000)
# Test Case 3: Equal head and tail speed
p.n1 = 15 # Head moves 15 LEDs/second
p.n2 = 25 # Max length 25 LEDs
p.n3 = 15 # Tail moves 15 LEDs/second
p.n4 = 3 # Min length 3 LEDs
p.colors = [(0, 0, 255)] # Blue
run_test_case(p, "Test 3: n1=15, n2=25, n3=15, n4=3", 8000)
# Test Case 4: Very fast head, slow tail
p.n1 = 30 # Head moves 30 LEDs/second
p.n2 = 40 # Max length 40 LEDs
p.n3 = 5 # Tail moves 5 LEDs/second
p.n4 = 8 # Min length 8 LEDs
p.colors = [(255, 255, 0)] # Yellow
run_test_case(p, "Test 4: n1=30, n2=40, n3=5, n4=8", 12000)
# Test Case 5: Long segment, fast cycle
p.n1 = 25 # Head moves 25 LEDs/second
p.n2 = 60 # Max length 60 LEDs
p.n3 = 30 # Tail moves 30 LEDs/second
p.n4 = 2 # Min length 2 LEDs
p.colors = [(255, 0, 255)] # Magenta
run_test_case(p, "Test 5: n1=25, n2=60, n3=30, n4=2", 10000)
# Test Case 6: Very slow head, very fast tail
p.n1 = 5 # Head moves 5 LEDs/second
p.n2 = 20 # Max length 20 LEDs
p.n3 = 40 # Tail moves 40 LEDs/second
p.n4 = 1 # Min length 1 LED
p.colors = [(0, 255, 255)] # Cyan
run_test_case(p, "Test 6: n1=5, n2=20, n3=40, n4=1", 8000)
# Test Case 7: Medium speeds, short segment
p.n1 = 12 # Head moves 12 LEDs/second
p.n2 = 15 # Max length 15 LEDs
p.n3 = 18 # Tail moves 18 LEDs/second
p.n4 = 4 # Min length 4 LEDs
p.colors = [(255, 128, 0)] # Orange
run_test_case(p, "Test 7: n1=12, n2=15, n3=18, n4=4", 6000)
# Test Case 8: Ultra fast everything
p.n1 = 100 # Head moves 100 LEDs/second
p.n2 = 50 # Max length 50 LEDs
p.n3 = 150 # Tail moves 150 LEDs/second
p.n4 = 10 # Min length 10 LEDs
p.colors = [(128, 0, 255)] # Purple
run_test_case(p, "Test 8: n1=100, n2=50, n3=150, n4=10", 5000)
print("\n=== All tests completed ===")
# Turn off LEDs
p.off()
print("LEDs turned off")
if __name__ == "__main__":
test_circle_loading()

56
test/flicker/1.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Flicker test 1: Red, delay=50ms, min brightness=50
Runs forever
Run with: mpremote run test/flicker/1.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 1: Red, delay=50ms, min brightness=50")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=50
)
# Configure test parameters
p.n1 = 50 # Min brightness 50
p.colors = [(255, 0, 0)] # Red
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/flicker/2.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Flicker test 2: Green, delay=100ms, min brightness=100
Runs forever
Run with: mpremote run test/flicker/2.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 2: Green, delay=100ms, min brightness=100")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=100
)
# Configure test parameters
p.n1 = 100 # Min brightness 100
p.colors = [(0, 255, 0)] # Green
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/flicker/3.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Flicker test 3: Blue, delay=200ms, min brightness=20
Runs forever
Run with: mpremote run test/flicker/3.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 3: Blue, delay=200ms, min brightness=20")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=200
)
# Configure test parameters
p.n1 = 20 # Min brightness 20
p.colors = [(0, 0, 255)] # Blue
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/flicker/4.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Flicker test 4: White, delay=80ms, min brightness=150
Runs forever
Run with: mpremote run test/flicker/4.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 4: White, delay=80ms, min brightness=150")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=80
)
# Configure test parameters
p.n1 = 150 # Min brightness 150
p.colors = [(255, 255, 255)] # White
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/flicker/5.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Flicker test 5: Yellow, delay=150ms, min brightness=30
Runs forever
Run with: mpremote run test/flicker/5.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 5: Yellow, delay=150ms, min brightness=30")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=150
)
# Configure test parameters
p.n1 = 30 # Min brightness 30
p.colors = [(255, 255, 0)] # Yellow
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/flicker/6.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Flicker test 6: Magenta, delay=60ms, min brightness=80
Runs forever
Run with: mpremote run test/flicker/6.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 6: Magenta, delay=60ms, min brightness=80")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=60
)
# Configure test parameters
p.n1 = 80 # Min brightness 80
p.colors = [(255, 0, 255)] # Magenta
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/flicker/7.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Flicker test 7: Cyan, delay=120ms, min brightness=5
Runs forever
Run with: mpremote run test/flicker/7.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 7: Cyan, delay=120ms, min brightness=5")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=120
)
# Configure test parameters
p.n1 = 5 # Min brightness 5
p.colors = [(0, 255, 255)] # Cyan
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/flicker/8.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Flicker test 8: Orange, delay=40ms, min brightness=180
Runs forever
Run with: mpremote run test/flicker/8.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Flicker Test 8: Orange, delay=40ms, min brightness=180")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=40
)
# Configure test parameters
p.n1 = 180 # Min brightness 180
p.colors = [(255, 128, 0)] # Orange
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Min brightness: {p.n1}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("fl")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

158
test/main.py Normal file
View File

@@ -0,0 +1,158 @@
import asyncio
import json
import argparse
import signal
try:
import websockets # type: ignore
except Exception as e:
print("Please install websockets: pip install websockets")
raise
WS_URI = "ws://192.168.4.1/ws"
# Default pattern suite aligned with current firmware patterns
PATTERN_SUITE = [
{"pattern": "flicker", "delay": 80, "iterations": 30, "repeat_delay": 80, "colors": ["#ffaa00"]},
{"pattern": "fill_range", "n1": 10, "n2": 20, "delay": 400, "iterations": 1, "repeat_delay": 500, "colors": ["#888888"]},
{"pattern": "n_chase", "n1": 5, "n2": 5, "delay": 250, "iterations": 40, "repeat_delay": 120, "colors": ["#00ff88"]},
{"pattern": "alternating", "n1": 6, "n2": 6, "delay": 300, "iterations": 20, "repeat_delay": 300, "colors": ["#ff8800"]},
{"pattern": "pulse", "delay": 200, "iterations": 6, "repeat_delay": 300, "colors": ["#ffffff"]},
]
def build_message(
pattern: str,
n: int | None = None,
delay: int | None = None,
colors: list[str] | None = None,
brightness: int | None = None,
num_leds: int | None = None,
n1: int | None = None,
n2: int | None = None,
name: str = "0",
pattern_step: int | None = None,
):
settings: dict[str, object] = {
"pattern": pattern,
}
if n is not None:
settings["n"] = n
if delay is not None:
settings["delay"] = delay
if colors is not None:
settings["colors"] = colors
if brightness is not None:
settings["brightness"] = brightness
if num_leds is not None:
settings["num_leds"] = num_leds
if n1 is not None:
settings["n1"] = n1
if n2 is not None:
settings["n2"] = n2
if pattern_step is not None:
settings["pattern_step"] = pattern_step
# ESP-NOW-style nested payload keyed by name (e.g., "0")
return {name: settings}
async def send_once(uri: str, payload: dict, hold_ms: int | None = None):
async with websockets.connect(uri) as ws:
await ws.send(json.dumps(payload))
if hold_ms and hold_ms > 0:
await asyncio.sleep(hold_ms / 1000)
async def run_suite(uri: str):
async with websockets.connect(uri) as ws:
for cfg in PATTERN_SUITE:
iterations = int(cfg.get("iterations", 10))
interval_ms = int(cfg.get("interval_ms", cfg.get("delay", 100) or 100))
repeat_ms = int(cfg.get("repeat_delay", interval_ms))
for i in range(iterations):
msg = build_message(
cfg.get("pattern", "off"),
i,
delay=cfg.get("delay"),
colors=cfg.get("colors"),
brightness=cfg.get("brightness", 255),
num_leds=cfg.get("num_leds"),
n1=cfg.get("n1"),
n2=cfg.get("n2"),
name=cfg.get("name", "0"),
pattern_step=cfg.get("pattern_step"),
)
print(msg)
await ws.send(json.dumps(msg))
await asyncio.sleep(repeat_ms / 1000)
def _parse_args():
p = argparse.ArgumentParser(description="WebSocket LED pattern tester")
p.add_argument("--uri", default=WS_URI, help="WebSocket URI, default ws://192.168.4.1/ws")
p.add_argument("--pattern", help="Single pattern to send (overrides suite)")
p.add_argument("--delay", type=int, help="Delay ms")
p.add_argument("--brightness", type=int, help="Brightness 0-255")
p.add_argument("--num-leds", type=int, help="Number of LEDs")
p.add_argument("--colors", nargs="*", help="Hex colors like #ff0000 #00ff00")
p.add_argument("--on-width", type=int)
p.add_argument("--off-width", type=int)
p.add_argument("--n1", type=int)
p.add_argument("--n2", type=int)
p.add_argument("--name", default="0", help="Target name key for nested payload (default: 0)")
p.add_argument("--iterations", type=int, help="How many cycles/messages to send")
p.add_argument("--interval", type=int, help="Interval between messages in ms (default: delay or 100)")
p.add_argument("--repeat-delay", dest="repeat_delay", type=int, help="Delay between repeats in ms (overrides --interval if set)")
p.add_argument("--hold", type=int, default=1500, help="Hold ms for single send")
return p.parse_args()
def _setup_sigint(loop: asyncio.AbstractEventLoop):
for sig in (signal.SIGINT, signal.SIGTERM):
try:
loop.add_signal_handler(sig, loop.stop)
except NotImplementedError:
pass
async def main_async():
args = _parse_args()
if args.pattern:
iterations = int(args.iterations or 1)
interval_ms = int(args.interval or (args.delay if args.delay is not None else 100))
repeat_ms = int(args.repeat_delay or interval_ms)
async with websockets.connect(args.uri) as ws:
for i in range(iterations):
msg = build_message(
pattern=args.pattern,
n=i,
delay=args.delay,
colors=args.colors,
brightness=args.brightness,
num_leds=args.num_leds,
n1=args.n1,
n2=args.n2,
name=args.name,
)
print(msg)
await ws.send(json.dumps(msg))
await asyncio.sleep(repeat_ms / 1000)
else:
await run_suite(args.uri)
def main():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
_setup_sigint(loop)
try:
loop.run_until_complete(main_async())
finally:
try:
loop.run_until_complete(asyncio.sleep(0))
except Exception:
pass
loop.close()
if __name__ == "__main__":
main()

58
test/n_chase/1.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
N-chase test 1: Red, on=5, off=5, delay=100ms
Runs forever
Run with: mpremote run test/n_chase/1.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 1: Red, on=5, off=5, delay=100ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=100
)
# Configure test parameters
p.n1 = 5 # On width 5
p.n2 = 5 # Off width 5
p.colors = [(255, 0, 0)] # Red
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/n_chase/2.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
N-chase test 2: Green, on=10, off=5, delay=150ms
Runs forever
Run with: mpremote run test/n_chase/2.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 2: Green, on=10, off=5, delay=150ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=150
)
# Configure test parameters
p.n1 = 10 # On width 10
p.n2 = 5 # Off width 5
p.colors = [(0, 255, 0)] # Green
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/n_chase/3.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
N-chase test 3: Blue, on=3, off=10, delay=80ms
Runs forever
Run with: mpremote run test/n_chase/3.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 3: Blue, on=3, off=10, delay=80ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=80
)
# Configure test parameters
p.n1 = 3 # On width 3
p.n2 = 10 # Off width 10
p.colors = [(0, 0, 255)] # Blue
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/n_chase/4.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
N-chase test 4: Yellow, on=7, off=7, delay=120ms
Runs forever
Run with: mpremote run test/n_chase/4.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 4: Yellow, on=7, off=7, delay=120ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=120
)
# Configure test parameters
p.n1 = 7 # On width 7
p.n2 = 7 # Off width 7
p.colors = [(255, 255, 0)] # Yellow
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/n_chase/5.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
N-chase test 5: White, on=2, off=8, delay=200ms
Runs forever
Run with: mpremote run test/n_chase/5.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 5: White, on=2, off=8, delay=200ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=200
)
# Configure test parameters
p.n1 = 2 # On width 2
p.n2 = 8 # Off width 8
p.colors = [(255, 255, 255)] # White
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/n_chase/6.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
N-chase test 6: Magenta, on=15, off=3, delay=60ms
Runs forever
Run with: mpremote run test/n_chase/6.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 6: Magenta, on=15, off=3, delay=60ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=60
)
# Configure test parameters
p.n1 = 15 # On width 15
p.n2 = 3 # Off width 3
p.colors = [(255, 0, 255)] # Magenta
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/n_chase/7.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
N-chase test 7: Cyan, on=4, off=12, delay=180ms
Runs forever
Run with: mpremote run test/n_chase/7.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 7: Cyan, on=4, off=12, delay=180ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=180
)
# Configure test parameters
p.n1 = 4 # On width 4
p.n2 = 12 # Off width 12
p.colors = [(0, 255, 255)] # Cyan
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

58
test/n_chase/8.py Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""
N-chase test 8: Orange, on=1, off=20, delay=250ms
Runs forever
Run with: mpremote run test/n_chase/8.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 8: Orange, on=1, off=20, delay=250ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=250
)
# Configure test parameters
p.n1 = 1 # On width 1
p.n2 = 20 # Off width 20
p.colors = [(255, 128, 0)] # Orange
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

62
test/n_chase/9.py Normal file
View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""
N-chase test 9: Cyan, on=20, off=20, delay=200ms
Runs forever
Run with: mpremote run test/n_chase/9.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting N-Chase Test 9: Cyan, on=20, off=20, delay=200ms")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=200
)
# Configure test parameters
p.n1 = 20 # On width 20
p.n2 = 20 # Off width 20
p.n3 = 20 # Reserved for future use
p.n4 = -5 # Reserved for future use
p.colors = [(0, 255, 255)] # Cyan
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"On width: {p.n1}")
print(f"Off width: {p.n2}")
print(f"n3: {p.n3}")
print(f"n4: {p.n4}")
print(f"Color: {p.colors[0]}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("nc")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

View File

@@ -1,55 +0,0 @@
#!/usr/bin/env python3
import uasyncio as asyncio
from machine import WDT
from settings import Settings
from patterns import Patterns
async def main():
s = Settings()
pin = s.get("led_pin", 10)
num = s.get("num_leds", 30)
p = Patterns(pin=pin, num_leds=num)
p.load()
print(p)
p.save()
# print(p)
# wdt = WDT(timeout=10000)
# # Baseline params
# p.set_param("br", 64)
# p.set_param("dl", 500)
# p.set_param("cl", [(255, 0, 0), (0, 0, 255)])
# p.set_param("n1", 200)
# p.set_param("n2", 200)
# p.set_param("n3", 1)
# p.set_param("n4", 1)
# for name, fn in p.patterns.items():
# if fn is None:
# continue
# print(name)
# p.set_param("pt", name)
# task = asyncio.create_task(p.run())
# end = asyncio.get_event_loop().time() + 2.0
# while asyncio.get_event_loop().time() < end:
# wdt.feed()
# await asyncio.sleep_ms(10)
# p.stopped = True
# await task
# p.stopped = False
# p.set_param("pt", "off")
# task = asyncio.create_task(p.run())
# await asyncio.sleep_ms(200)
# p.stopped = True
# await task
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,32 +0,0 @@
#!/usr/bin/env python3
import uasyncio as asyncio
import utime
from machine import WDT
from settings import Settings
from patterns import Patterns
async def main():
s = Settings()
pin = s.get("led_pin", 10)
num = s.get("num_leds", 30)
p = Patterns(pin=pin, num_leds=num)
wdt = WDT(timeout=10000)
p.set_param("br", 64)
p.set_param("dl", 200)
p.set_param("cl", [(255, 0, 0), (0, 0, 255)])
p.select("blink")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 1500:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,138 +0,0 @@
#!/usr/bin/env python3
import uasyncio as asyncio
import utime
from machine import WDT
from settings import Settings
from patterns import Patterns
async def main():
s = Settings()
pin = s.get("led_pin", 10)
num = s.get("num_leds", 30)
p = Patterns(pin=pin, num_leds=num)
wdt = WDT(timeout=10000)
# Test 1: Basic n_chase (n1=5, n2=5, n3=1, n4=1)
print("Test 1: Basic n_chase (n1=5, n2=5, n3=1, n4=1)")
p.set_param("br", 255)
p.set_param("dl", 200)
p.set_param("n1", 5) # 5 LEDs color0
p.set_param("n2", 5) # 5 LEDs color1
p.set_param("n3", 1) # Move 1 forward on even steps
p.set_param("n4", 1) # Move 1 forward on odd steps
p.set_param("cl", [(255, 0, 0), (0, 255, 0)]) # Red and Green
p.select("n_chase")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 2: Forward and backward (n3=2, n4=-1)
print("Test 2: Forward and backward (n3=2, n4=-1)")
p.stopped = False
p.set_param("n1", 3)
p.set_param("n2", 3)
p.set_param("n3", 2) # Move 2 forward on even steps
p.set_param("n4", -1) # Move 1 backward on odd steps
p.set_param("dl", 150)
p.set_param("cl", [(0, 0, 255), (255, 255, 0)]) # Blue and Yellow
p.select("n_chase")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 3: Large segments (n1=10, n2=5)
print("Test 3: Large segments (n1=10, n2=5, n3=3, n4=3)")
p.stopped = False
p.set_param("n1", 10) # 10 LEDs color0
p.set_param("n2", 5) # 5 LEDs color1
p.set_param("n3", 3) # Move 3 forward
p.set_param("n4", 3) # Move 3 forward
p.set_param("dl", 200)
p.set_param("cl", [(255, 128, 0), (128, 0, 255)]) # Orange and Purple
p.select("n_chase")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 4: Fast movement (n3=5, n4=5)
print("Test 4: Fast movement (n3=5, n4=5)")
p.stopped = False
p.set_param("n1", 4)
p.set_param("n2", 4)
p.set_param("n3", 5) # Move 5 forward
p.set_param("n4", 5) # Move 5 forward
p.set_param("dl", 100)
p.set_param("cl", [(255, 0, 255), (0, 255, 255)]) # Magenta and Cyan
p.select("n_chase")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 2000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 5: Backward movement (n3=-2, n4=-2)
print("Test 5: Backward movement (n3=-2, n4=-2)")
p.stopped = False
p.set_param("n1", 6)
p.set_param("n2", 4)
p.set_param("n3", -2) # Move 2 backward
p.set_param("n4", -2) # Move 2 backward
p.set_param("dl", 200)
p.set_param("cl", [(255, 255, 255), (0, 0, 0)]) # White and Black
p.select("n_chase")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 6: Alternating forward/backward (n3=3, n4=-2)
print("Test 6: Alternating forward/backward (n3=3, n4=-2)")
p.stopped = False
p.set_param("n1", 5)
p.set_param("n2", 5)
p.set_param("n3", 3) # Move 3 forward on even steps
p.set_param("n4", -2) # Move 2 backward on odd steps
p.set_param("dl", 250)
p.set_param("cl", [(255, 0, 0), (0, 255, 0)]) # Red and Green
p.select("n_chase")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 4000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Cleanup
print("Test complete, turning off")
p.stopped = False
p.select("off")
task = asyncio.create_task(p.run())
await asyncio.sleep_ms(100)
await p.stop()
await task
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env python3
import uasyncio as asyncio
from machine import WDT
from settings import Settings
from patterns import Patterns
async def main():
s = Settings()
pin = s.get("led_pin", 10)
num = s.get("num_leds", 30)
p = Patterns(pin=pin, num_leds=num)
wdt = WDT(timeout=10000)
p.select("off")
task = asyncio.create_task(p.run())
wdt.feed()
await asyncio.sleep_ms(200)
p.stopped = True
await task
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,34 +0,0 @@
#!/usr/bin/env python3
import uasyncio as asyncio
from machine import WDT
from settings import Settings
from patterns import Patterns
async def main():
s = Settings()
pin = s.get("led_pin", 10)
num = s.get("num_leds", 30)
p = Patterns(pin=pin, num_leds=num)
wdt = WDT(timeout=10000)
p.set_param("br", 64)
p.set_param("dl", 120)
p.set_param("cl", [(255, 0, 0), (0, 0, 255)])
p.select("on")
task = asyncio.create_task(p.run())
await asyncio.sleep_ms(800)
p.stopped = True
await task
p.stopped = False
p.select("off")
task = asyncio.create_task(p.run())
await asyncio.sleep_ms(100)
p.stopped = True
await task
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,160 +0,0 @@
#!/usr/bin/env python3
import uasyncio as asyncio
import utime
from machine import WDT
from settings import Settings
from patterns import Patterns
async def main():
s = Settings()
pin = s.get("led_pin", 10)
num = s.get("num_leds", 30)
p = Patterns(pin=pin, num_leds=num)
wdt = WDT(timeout=10000)
# Test 1: Basic pulse with attack, hold, and decay
print("Test 1: Basic pulse pattern")
p.set_param("br", 255)
p.set_param("dl", 1000) # 1 second delay between pulses
p.set_param("auto", True) # Run continuously
p.set_param("cl", [(255, 255, 255), (255, 255, 255)])
p.set_param("n1", 200) # Attack: 200ms
p.set_param("n2", 200) # Hold: 200ms
p.set_param("n3", 200) # Decay: 200ms
p.select("pulse")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 3 seconds to see multiple pulse cycles
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Test 2: Fast pulse with shorter delay
print("Test 2: Fast pulse pattern")
p.stopped = False
p.set_param("dl", 500) # 500ms delay between pulses
p.set_param("auto", True) # Run continuously
p.set_param("n1", 100) # Attack: 100ms
p.set_param("n2", 100) # Hold: 100ms
p.set_param("n3", 100) # Decay: 100ms
p.select("pulse")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 2 seconds
while utime.ticks_diff(utime.ticks_ms(), start) < 2000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Test 3: Colored pulse
print("Test 3: Colored pulse pattern")
p.stopped = False
p.set_param("dl", 800)
p.set_param("auto", True) # Run continuously
p.set_param("cl", [(255, 0, 0), (0, 0, 255)]) # Red pulse
p.set_param("n1", 150)
p.set_param("n2", 150)
p.set_param("n3", 150)
p.select("pulse")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 2 seconds
while utime.ticks_diff(utime.ticks_ms(), start) < 2000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Test 4: Verify delay restart timing
print("Test 4: Testing delay restart timing")
p.stopped = False
p.set_param("dl", 500) # 500ms delay
p.set_param("auto", True) # Run continuously
p.set_param("n1", 100) # Total attack+hold+decay = 300ms, should wait 200ms more
p.set_param("n2", 100)
p.set_param("n3", 100)
p.select("pulse")
task = asyncio.create_task(p.run())
# Monitor pulse cycles
cycle_count = 0
last_cycle_time = utime.ticks_ms()
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
# Check if we're near the start of a new cycle (LEDs off)
# This is a simplified check - in practice you'd monitor LED state
p.stopped = True
await task
# Test 5: Single-shot pulse (auto=False)
print("Test 5: Single-shot pulse (auto=False)")
p.stopped = False
p.set_param("dl", 500) # Delay between pulses
p.set_param("auto", False) # Run only once
p.set_param("cl", [(0, 255, 0), (0, 255, 0)]) # Green pulse
p.set_param("n1", 150) # Attack: 150ms
p.set_param("n2", 150) # Hold: 150ms
p.set_param("n3", 150) # Decay: 150ms
p.select("pulse")
task = asyncio.create_task(p.run())
# The pulse should complete once and then stop
# Total time should be ~450ms (attack + hold + decay)
# Wait a bit longer to verify it doesn't repeat
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 1000:
wdt.feed()
await asyncio.sleep_ms(10)
# Task should have completed on its own (not stopped manually)
# Verify it's stopped
if not p.stopped:
print("Warning: Pulse should have stopped automatically with auto=False")
p.stopped = True
await task
# Test 6: Pulse cycles through colors
print("Test 6: Pulse cycles through colors")
p.stopped = False
p.set_param("dl", 300) # cycle interval
p.set_param("auto", True) # Run continuously
p.set_param("cl", [
(255, 0, 0), # red
(0, 255, 0), # green
(0, 0, 255), # blue
(255, 255, 0), # yellow
])
p.set_param("n1", 50)
p.set_param("n2", 0)
p.set_param("n3", 50)
p.select("pulse")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run long enough to observe multiple color cycles
while utime.ticks_diff(utime.ticks_ms(), start) < 10000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Cleanup
print("Test complete, turning off")
p.stopped = False
p.select("off")
task = asyncio.create_task(p.run())
await asyncio.sleep_ms(100)
p.stopped = True
await task
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,167 +0,0 @@
#!/usr/bin/env python3
import uasyncio as asyncio
import utime
from machine import WDT
from settings import Settings
from patterns import Patterns
async def main():
s = Settings()
pin = s.get("led_pin", 10)
num = s.get("num_leds", 30)
p = Patterns(pin=pin, num_leds=num)
wdt = WDT(timeout=10000)
# Test 1: Basic rainbow with auto=True (continuous)
print("Test 1: Basic rainbow (auto=True, n1=1)")
p.set_param("br", 255)
p.set_param("dl", 100) # Delay affects animation speed
p.set_param("n1", 1) # Step increment of 1
p.set_param("auto", True) # Run continuously
p.select("rainbow")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 3 seconds to see rainbow animation
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 2: Fast rainbow
print("Test 2: Fast rainbow (low delay, n1=1)")
p.stopped = False
p.set_param("dl", 50) # Faster animation
p.set_param("n1", 1)
p.set_param("auto", True)
p.select("rainbow")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 2000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 3: Slow rainbow
print("Test 3: Slow rainbow (high delay, n1=1)")
p.stopped = False
p.set_param("dl", 500) # Slower animation
p.set_param("n1", 1)
p.set_param("auto", True)
p.select("rainbow")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 4: Low brightness rainbow
print("Test 4: Low brightness rainbow (n1=1)")
p.stopped = False
p.set_param("br", 64) # Low brightness
p.set_param("dl", 100)
p.set_param("n1", 1)
p.set_param("auto", True)
p.select("rainbow")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 2000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 5: Single-step rainbow (auto=False)
print("Test 5: Single-step rainbow (auto=False, n1=1)")
p.stopped = False
p.set_param("br", 255)
p.set_param("dl", 100)
p.set_param("n1", 1)
p.set_param("auto", False) # Run once per call
p.set_param("step", 0) # Reset step
p.select("rainbow")
# Call rainbow multiple times to see step progression
for i in range(10):
task = asyncio.create_task(p.run())
await task
await asyncio.sleep_ms(100) # Small delay between steps
wdt.feed()
# Test 6: Verify step updates correctly
print("Test 6: Verify step updates (auto=False, n1=1)")
p.stopped = False
p.set_param("n1", 1)
initial_step = p.step
p.select("rainbow")
task = asyncio.create_task(p.run())
await task
final_step = p.step
print(f"Step updated from {initial_step} to {final_step} (expected increment: 1)")
# Test 7: Fast step increment (n1=5)
print("Test 7: Fast rainbow (n1=5, auto=True)")
p.stopped = False
p.set_param("br", 255)
p.set_param("dl", 100)
p.set_param("n1", 5) # Step increment of 5 (5x faster)
p.set_param("auto", True)
p.select("rainbow")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 2000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 8: Very fast step increment (n1=10)
print("Test 8: Very fast rainbow (n1=10, auto=True)")
p.stopped = False
p.set_param("n1", 10) # Step increment of 10 (10x faster)
p.set_param("auto", True)
p.select("rainbow")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 2000:
wdt.feed()
await asyncio.sleep_ms(10)
await p.stop()
await task
# Test 9: Verify n1 controls step increment (auto=False)
print("Test 9: Verify n1 step increment (auto=False, n1=5)")
p.stopped = False
p.set_param("n1", 5) # Step increment of 5
p.set_param("auto", False)
p.set_param("step", 0) # Reset step
initial_step = p.step
p.select("rainbow")
task = asyncio.create_task(p.run())
await task
final_step = p.step
expected_step = (initial_step + 5) % 256
print(f"Step updated from {initial_step} to {final_step} (expected: {expected_step})")
if final_step == expected_step:
print("✓ n1 step increment working correctly")
else:
print(f"✗ Step increment mismatch! Expected {expected_step}, got {final_step}")
# Cleanup
print("Test complete, turning off")
p.stopped = False
p.select("off")
task = asyncio.create_task(p.run())
await asyncio.sleep_ms(100)
await p.stop()
await task
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,165 +0,0 @@
#!/usr/bin/env python3
import uasyncio as asyncio
import utime
from machine import WDT
from settings import Settings
from patterns import Patterns
async def main():
s = Settings()
pin = s.get("led_pin", 10)
num = s.get("num_leds", 30)
p = Patterns(pin=pin, num_leds=num)
wdt = WDT(timeout=10000)
# Test 1: Basic transition with 2 colors (auto=True, cycles continuously)
print("Test 1: Basic transition (2 colors, 1000ms delay, auto=True)")
p.set_param("br", 255)
p.set_param("dl", 1000) # 1 second transition time
p.set_param("auto", True) # Cycle continuously
p.set_param("cl", [(255, 0, 0), (0, 255, 0)]) # Red to Green
p.select("transition")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 5 seconds to see multiple transitions
while utime.ticks_diff(utime.ticks_ms(), start) < 5000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Test 2: Fast transition (auto=True, cycles continuously)
print("Test 2: Fast transition (500ms delay, auto=True)")
p.stopped = False
p.set_param("dl", 500) # 500ms transition time
p.set_param("auto", True) # Cycle continuously
p.set_param("cl", [(0, 0, 255), (255, 255, 0)]) # Blue to Yellow
p.select("transition")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 3 seconds
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Test 3: Multiple colors transition (auto=True, cycles continuously)
print("Test 3: Multiple colors transition (3 colors, auto=True)")
p.stopped = False
p.set_param("dl", 800)
p.set_param("auto", True) # Cycle continuously
p.set_param("cl", [
(255, 0, 0), # Red
(0, 255, 0), # Green
(0, 0, 255), # Blue
])
p.select("transition")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 8 seconds to see full cycles
while utime.ticks_diff(utime.ticks_ms(), start) < 8000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Test 4: Single color (should just stay that color)
print("Test 4: Single color (should stay that color)")
p.stopped = False
p.set_param("dl", 1000)
p.set_param("cl", [(255, 128, 0)]) # Orange
p.select("transition")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 3 seconds
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Test 5: Many colors transition (auto=True, cycles continuously)
print("Test 5: Many colors transition (5 colors, auto=True)")
p.stopped = False
p.set_param("dl", 600)
p.set_param("auto", True) # Cycle continuously
p.set_param("cl", [
(255, 0, 0), # Red
(255, 128, 0), # Orange
(255, 255, 0), # Yellow
(0, 255, 0), # Green
(0, 0, 255), # Blue
])
p.select("transition")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 10 seconds to see multiple cycles
while utime.ticks_diff(utime.ticks_ms(), start) < 10000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Test 6: Low brightness transition (auto=True, cycles continuously)
print("Test 6: Low brightness transition (auto=True)")
p.stopped = False
p.set_param("br", 64) # Low brightness
p.set_param("dl", 1000)
p.set_param("auto", True) # Cycle continuously
p.set_param("cl", [(255, 0, 0), (0, 255, 0)])
p.select("transition")
task = asyncio.create_task(p.run())
start = utime.ticks_ms()
# Run for 3 seconds
while utime.ticks_diff(utime.ticks_ms(), start) < 3000:
wdt.feed()
await asyncio.sleep_ms(10)
p.stopped = True
await task
# Test 7: Single-shot transition (auto=False, only color1 to color2)
print("Test 7: Single-shot transition (auto=False, color1 to color2 only)")
p.stopped = False
p.set_param("br", 255)
p.set_param("dl", 1000) # 1 second transition
p.set_param("auto", False) # Run only once
p.set_param("cl", [
(255, 0, 0), # Red (color1)
(0, 255, 0), # Green (color2)
(0, 0, 255), # Blue (should be ignored)
(255, 255, 0), # Yellow (should be ignored)
])
p.select("transition")
task = asyncio.create_task(p.run())
# The transition should complete once (color1 to color2) and then stop
# Total time should be ~1000ms
# Wait a bit longer to verify it doesn't continue
start = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start) < 2000:
wdt.feed()
await asyncio.sleep_ms(10)
# Task should have completed on its own (not stopped manually)
# Verify it's stopped
if not p.stopped:
print("Warning: Transition should have stopped automatically with auto=False")
p.stopped = True
await task
# Cleanup
print("Test complete, turning off")
p.stopped = False
p.select("off")
task = asyncio.create_task(p.run())
await asyncio.sleep_ms(100)
p.stopped = True
await task
if __name__ == "__main__":
asyncio.run(main())

56
test/rainbow/1.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Rainbow test 1: Single node (full spectrum), 3 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/1.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 1: Single node (full spectrum), 3 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=3000 # 3 second cycle
)
# Configure test parameters
p.n1 = 1 # 1 node (full spectrum)
p.delay = 3000 # 3 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/rainbow/2.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Rainbow test 2: Two nodes, 2 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/2.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 2: Two nodes, 2 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000 # 2 second cycle
)
# Configure test parameters
p.n1 = 2 # 2 nodes
p.delay = 2000 # 2 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/rainbow/3.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Rainbow test 3: Three nodes (RGB), 1.5 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/3.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 3: Three nodes (RGB), 1.5 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=1500 # 1.5 second cycle
)
# Configure test parameters
p.n1 = 3 # 3 nodes
p.delay = 1500 # 1.5 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/rainbow/4.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Rainbow test 4: Four nodes (RYGB), 1 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/4.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 4: Four nodes (RYGB), 1 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=1000 # 1 second cycle
)
# Configure test parameters
p.n1 = 4 # 4 nodes
p.delay = 1000 # 1 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/rainbow/5.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Rainbow test 5: Six nodes (ROYGBP), 0.8 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/5.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 5: Six nodes (ROYGBP), 0.8 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=800 # 0.8 second cycle
)
# Configure test parameters
p.n1 = 6 # 6 nodes
p.delay = 800 # 0.8 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/rainbow/6.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Rainbow test 6: Eight nodes, 0.6 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/6.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 6: Eight nodes, 0.6 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=600 # 0.6 second cycle
)
# Configure test parameters
p.n1 = 8 # 8 nodes
p.delay = 600 # 0.6 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/rainbow/7.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Rainbow test 7: Twelve nodes (fine gradation), 0.4 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/7.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 7: Twelve nodes (fine gradation), 0.4 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=400 # 0.4 second cycle
)
# Configure test parameters
p.n1 = 12 # 12 nodes
p.delay = 400 # 0.4 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/rainbow/8.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Rainbow test 8: Many nodes (20), slow cycle (2 seconds), full brightness
Runs forever
Run with: mpremote run test/rainbow/8.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 8: Many nodes (20), slow cycle (2 seconds)")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=2000 # 2 second cycle
)
# Configure test parameters
p.n1 = 20 # 20 nodes
p.delay = 2000 # 2 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

56
test/rainbow/9.py Normal file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""
Rainbow test 9: Five nodes, 1 second cycle, full brightness
Runs forever
Run with: mpremote run test/rainbow/9.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
print("Starting Rainbow Test 9: Five nodes, 1 second cycle")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=1000 # 1 second cycle
)
# Configure test parameters
p.n1 = 5 # 5 nodes
p.delay = 1000 # 1 second cycle
p.brightness = 255 # Full brightness
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Brightness: {p.brightness}")
print(f"Delay: {p.delay}ms")
print(f"Nodes: {p.n1}")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("rb")
print("Pattern started. Running forever...")
# Run forever
try:
while True:
wdt.feed()
utime.sleep_ms(100)
except KeyboardInterrupt:
print("\nStopping...")
p.run = False
p.off()
print("LEDs turned off")

100
test/rainbow/main.py Normal file
View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""
Rainbow test suite - runs all tests in sequence forever
Run with: mpremote run test/rainbow/main.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
def run_test_case(p, test_name, n1, delay, brightness, duration_ms=6000):
"""Run a test case for specified duration"""
print(f"\n--- {test_name} ---")
print(f"Parameters: n1={n1} nodes, delay={delay}ms cycle time, brightness={brightness}")
# Configure parameters
p.n1 = n1
p.delay = delay
p.brightness = brightness
# Restart the pattern with new parameters
p.run = False
while p.running:
utime.sleep_ms(1)
p.select("rb")
# Run for specified duration
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < duration_ms:
wdt.feed()
utime.sleep_ms(100)
# Stop pattern
p.run = False
print(f"{test_name} completed")
# Brief pause between tests
utime.sleep_ms(500)
def test_rainbow_suite():
"""Run all rainbow tests continuously"""
print("Starting rainbow test suite...")
print("Runs all tests in sequence, then repeats")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255,
delay=1000
)
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Base Brightness: {p.brightness}")
# Initialize watchdog timer
global wdt
wdt = WDT(timeout=10000)
wdt.feed()
# Test configurations
tests = [
("Test 1: Single node (full spectrum)", 1, 3000, 255, 12000),
("Test 2: Two nodes", 2, 2000, 255, 10000),
("Test 3: Three nodes (RGB)", 3, 1500, 255, 9000),
("Test 4: Four nodes (RYGB)", 4, 1000, 255, 8000),
("Test 5: Six nodes (ROYGBP)", 6, 800, 255, 8000),
("Test 6: Eight nodes", 8, 600, 255, 6000),
("Test 7: Twelve nodes (fine gradation)", 12, 400, 255, 6000),
("Test 8: Many nodes (20), slow cycle", 20, 2000, 255, 10000),
("Test 9: Five nodes", 5, 1000, 255, 6000),
]
cycle = 0
try:
while True:
cycle += 1
print(f"\n\n========== Starting Cycle {cycle} ==========")
for test_name, n1, delay, brightness, duration in tests:
run_test_case(p, test_name, n1, delay, brightness, duration)
print("\n========== Cycle completed ==========\n")
except KeyboardInterrupt:
print("\n\nStopping test suite...")
p.run = False
p.off()
print("LEDs turned off")
if __name__ == "__main__":
test_rainbow_suite()

160
test/random_test.py Normal file
View File

@@ -0,0 +1,160 @@
#!/usr/bin/env python3
"""
Random test - randomly selects and runs test patterns for 2-10 minutes each
Run with: mpremote run test/random_test.py
"""
import patterns
import utime
import random
import math
from settings import Settings
from machine import WDT
def run_pattern(p, pattern_name, config, duration_ms):
"""Run a pattern for specified duration"""
print(f"\n{'='*60}")
print(f"Starting: {pattern_name}")
print(f"Duration: {duration_ms//1000} seconds")
# Configure parameters
if pattern_name == "blink":
p.delay = config.get("delay", 500)
elif pattern_name == "circle_loading":
p.n1 = config.get("n1", 50)
p.n2 = config.get("n2", 100)
p.n3 = config.get("n3", 200)
p.n4 = config.get("n4", 0)
p.delay = config.get("delay", 2000)
elif pattern_name == "sine_brightness":
p.n1 = config.get("n1", 50)
p.brightness = config.get("brightness", 255)
p.delay = config.get("delay", 1000)
elif pattern_name == "rainbow":
p.n1 = config.get("n1", 1)
p.delay = config.get("delay", 1000)
elif pattern_name == "flicker":
p.n1 = config.get("n1", 50)
p.delay = config.get("delay", 50)
elif pattern_name == "n_chase":
p.n1 = config.get("n1", 5)
p.n2 = config.get("n2", 5)
p.n3 = config.get("n3", 1)
p.n4 = config.get("n4", 1)
p.delay = config.get("delay", 100)
# Set color if provided
if "color" in config:
p.colors = [config["color"]]
# Start pattern
p.select(config.get("short_name", "o"))
# Run for duration
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < duration_ms:
wdt.feed()
utime.sleep_ms(100)
# Stop pattern
p.run = False
while p.running:
utime.sleep_ms(1)
p.off()
print(f"Completed: {pattern_name}")
utime.sleep_ms(500)
def random_test():
"""Run random test patterns continuously"""
print("Starting Random Test Suite")
print("Patterns will change every 1-5 minutes")
print("Press Ctrl+C to stop")
# Load settings
settings = Settings()
# Initialize patterns
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255
)
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
# Initialize watchdog
global wdt
wdt = WDT(timeout=10000)
wdt.feed()
# Define all test configurations
test_configs = [
# Circle loading patterns
{"pattern": "circle_loading", "short_name": "cl", "n1": 50, "n2": 100, "n3": 200, "n4": 0, "delay": 2000, "color": (255, 0, 0)},
{"pattern": "circle_loading", "short_name": "cl", "n1": 30, "n2": 60, "n3": 100, "n4": 0, "delay": 1500, "color": (0, 255, 0)},
{"pattern": "circle_loading", "short_name": "cl", "n1": 75, "n2": 150, "n3": 300, "n4": 10, "delay": 3000, "color": (0, 0, 255)},
# Sine brightness patterns
{"pattern": "sine_brightness", "short_name": "sb", "n1": 50, "brightness": 255, "delay": 1000, "color": (255, 0, 0)},
{"pattern": "sine_brightness", "short_name": "sb", "n1": 100, "brightness": 255, "delay": 800, "color": (0, 255, 0)},
{"pattern": "sine_brightness", "short_name": "sb", "n1": 0, "brightness": 255, "delay": 1500, "color": (0, 0, 255)},
# Rainbow patterns
{"pattern": "rainbow", "short_name": "rb", "n1": 1, "delay": 3000, "color": None},
{"pattern": "rainbow", "short_name": "rb", "n1": 2, "delay": 2000, "color": None},
{"pattern": "rainbow", "short_name": "rb", "n1": 3, "delay": 1500, "color": None},
{"pattern": "rainbow", "short_name": "rb", "n1": 6, "delay": 1000, "color": None},
{"pattern": "rainbow", "short_name": "rb", "n1": 12, "delay": 800, "color": None},
# Flicker patterns
{"pattern": "flicker", "short_name": "fl", "n1": 50, "delay": 50, "color": (255, 0, 0)},
{"pattern": "flicker", "short_name": "fl", "n1": 30, "delay": 100, "color": (0, 255, 0)},
{"pattern": "flicker", "short_name": "fl", "n1": 100, "delay": 30, "color": (0, 0, 255)},
# N-chase patterns (mix of forward-only and bidirectional)
# Forward-only patterns
{"pattern": "n_chase", "short_name": "nc", "n1": 5, "n2": 5, "n3": 5, "n4": 5, "delay": 100, "color": (255, 0, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 10, "n2": 5, "n3": 10, "n4": 10, "delay": 150, "color": (0, 255, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 20, "n2": 20, "n3": 20, "n4": 20, "delay": 200, "color": (0, 255, 255)},
# Bidirectional patterns
{"pattern": "n_chase", "short_name": "nc", "n1": 15, "n2": 10, "n3": 10, "n4": 5, "delay": 180, "color": (255, 255, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 20, "n2": 20, "n3": 20, "n4": 5, "delay": 200, "color": (255, 0, 255)},
{"pattern": "n_chase", "short_name": "nc", "n1": 12, "n2": 8, "n3": 15, "n4": 8, "delay": 160, "color": (255, 128, 128)},
# N-chase patterns (forward only, large segments)
{"pattern": "n_chase", "short_name": "nc", "n1": 30, "n2": 10, "n3": 5, "n4": 5, "delay": 150, "color": (255, 128, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 40, "n2": 20, "n3": 10, "n4": 10, "delay": 180, "color": (128, 255, 0)},
{"pattern": "n_chase", "short_name": "nc", "n1": 50, "n2": 25, "n3": 20, "n4": 20, "delay": 200, "color": (0, 128, 255)},
{"pattern": "n_chase", "short_name": "nc", "n1": 60, "n2": 30, "n3": 30, "n4": 30, "delay": 220, "color": (255, 0, 128)},
{"pattern": "n_chase", "short_name": "nc", "n1": 80, "n2": 40, "n3": 50, "n4": 50, "delay": 250, "color": (128, 0, 255)},
{"pattern": "n_chase", "short_name": "nc", "n1": 100, "n2": 50, "n3": 100, "n4": 100, "delay": 300, "color": (255, 255, 255)},
]
try:
cycle = 0
while True:
cycle += 1
print(f"\n\n{'='*60}")
print(f"Random Test Cycle {cycle}")
print(f"{'='*60}")
# Randomly select a test configuration
config = random.choice(test_configs)
# Random duration between 1-5 minutes (60,000 - 300,000 ms)
duration_ms = random.randint(60000, 300000)
# Run the selected pattern
run_pattern(p, config["pattern"], config, duration_ms)
except KeyboardInterrupt:
print("\n\nStopping random test suite...")
p.run = False
p.off()
print("LEDs turned off")
if __name__ == "__main__":
random_test()

120
test/sine_brightness.py Normal file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""
Test script for sine brightness pattern
Run with: mpremote run test/sine_brightness.py
"""
import patterns
import utime
from settings import Settings
from machine import WDT
def run_test_case(p, test_name, duration_ms=5000):
"""Run a test case for specified duration"""
print(f"\n--- {test_name} ---")
print(f"Parameters: n1={p.n1} min brightness, brightness={p.brightness} max brightness, delay={p.delay}ms wavelength")
print(f"Color: {p.colors[0]}")
print(f"Running for {duration_ms//1000} seconds...")
# Initialize watchdog timer
wdt = WDT(timeout=10000)
wdt.feed()
# Start pattern
p.select("sb")
# Run for specified duration
start_time = utime.ticks_ms()
while utime.ticks_diff(utime.ticks_ms(), start_time) < duration_ms:
wdt.feed()
utime.sleep_ms(100)
# Stop pattern
p.run = False
print(f"{test_name} completed")
# Brief pause between tests
utime.sleep_ms(500)
def test_sine_brightness():
print("Testing sine brightness pattern with multiple configurations...")
# Load settings
settings = Settings()
# Initialize patterns using settings
p = patterns.Patterns(
pin=settings["led_pin"],
num_leds=settings["num_leds"],
brightness=255, # Full brightness
delay=1000 # Base wavelength
)
print(f"LED Pin: {settings['led_pin']}")
print(f"LEDs: {settings['num_leds']}")
print(f"Base Brightness: {p.brightness}")
# Test Case 1: Slow breathing
p.n1 = 50 # Min brightness 50
p.brightness = 255 # Max brightness 255
p.delay = 3000 # 3 second cycle
p.colors = [(255, 0, 0)] # Red
run_test_case(p, "Test 1: Slow breathing (50-255 brightness)", 12000)
# Test Case 2: Fast pulsing
p.n1 = 100 # Min brightness 100
p.brightness = 255 # Max brightness 255
p.delay = 500 # 0.5 second cycle
p.colors = [(0, 255, 0)] # Green
run_test_case(p, "Test 2: Fast pulsing (100-255 brightness)", 8000)
# Test Case 3: Medium breathing
p.n1 = 80 # Min brightness 80
p.brightness = 255 # Max brightness 255
p.delay = 1500 # 1.5 second cycle
p.colors = [(0, 0, 255)] # Blue
run_test_case(p, "Test 3: Medium breathing (80-255 brightness)", 10000)
# Test Case 4: Very slow breathing
p.n1 = 150 # Min brightness 150
p.brightness = 255 # Max brightness 255
p.delay = 5000 # 5 second cycle
p.colors = [(255, 255, 0)] # Yellow
run_test_case(p, "Test 4: Very slow breathing (150-255 brightness)", 15000)
# Test Case 5: Very fast pulsing
p.n1 = 50 # Min brightness 50
p.brightness = 255 # Max brightness 255
p.delay = 200 # 0.2 second cycle
p.colors = [(255, 0, 255)] # Magenta
run_test_case(p, "Test 5: Very fast pulsing (50-255 brightness)", 6000)
# Test Case 6: Low range, slow cycle
p.n1 = 200 # Min brightness 200
p.brightness = 255 # Max brightness 255
p.delay = 4000 # 4 second cycle
p.colors = [(0, 255, 255)] # Cyan
run_test_case(p, "Test 6: Low range, slow cycle (200-255 brightness)", 12000)
# Test Case 7: High range, medium cycle
p.n1 = 100 # Min brightness 100
p.brightness = 255 # Max brightness 255
p.delay = 1000 # 1 second cycle
p.colors = [(255, 128, 0)] # Orange
run_test_case(p, "Test 7: High range, medium cycle (100-255 brightness)", 8000)
# Test Case 8: Ultra fast strobe
p.n1 = 100 # Min brightness 100
p.brightness = 255 # Max brightness 255
p.delay = 100 # 0.1 second cycle
p.colors = [(128, 0, 255)] # Purple
run_test_case(p, "Test 8: Ultra fast strobe (100-255 brightness)", 4000)
print("\n=== All tests completed ===")
# Turn off LEDs
p.off()
print("LEDs turned off")
if __name__ == "__main__":
test_sine_brightness()

View File

@@ -1,110 +0,0 @@
#!/usr/bin/env python3
"""
Test for saving and loading patterns
Run with: mpremote run test/test_patterns_save_load.py
"""
import json
import uasyncio as asyncio
from settings import Settings
from patterns import Patterns
async def test_patterns_save_load():
"""Test saving and loading patterns"""
print("Testing patterns save and load functionality...")
# Test 1: Initialize patterns and check initial state
print("\nTest 1: Initialize patterns")
s = Settings()
pin = s.get("led_pin", 10)
num_leds = s.get("num_leds", 30)
p1 = Patterns(pin=pin, num_leds=num_leds)
print(f"Initial patterns count: {len(p1.patterns)}")
print(f"Available patterns: {list(p1.patterns.keys())}")
print(f"Selected pattern: {p1.selected}")
# Test 2: Try to save patterns (will fail because patterns contain functions)
print("\nTest 2: Attempt to save patterns")
try:
result = p1.save()
if result:
print("✓ Patterns saved successfully")
else:
print("✗ Patterns save failed (expected - patterns contain functions)")
except Exception as e:
print(f"✗ Exception during save: {e}")
# Test 3: Try to load patterns
print("\nTest 3: Attempt to load patterns")
try:
result = p1.load()
if result:
print("✓ Patterns loaded successfully")
print(f"Patterns after load: {list(p1.patterns.keys())}")
else:
print("✗ Patterns load failed")
except Exception as e:
print(f"✗ Exception during load: {e}")
# Test 4: Test with empty patterns dict (simulating custom patterns)
print("\nTest 4: Test save/load with empty patterns dict")
p2 = Patterns(pin=pin, num_leds=num_leds)
# Store original patterns
original_patterns = p2.patterns.copy()
# Clear patterns to test save/load with empty dict
p2.patterns = {}
try:
result = p2.save()
if result:
print("✓ Empty patterns dict saved successfully")
else:
print("✗ Failed to save empty patterns dict")
except Exception as e:
print(f"✗ Exception saving empty patterns: {e}")
# Try to load
p3 = Patterns(pin=pin, num_leds=num_leds)
p3.patterns = {} # Start with empty
try:
result = p3.load()
if result:
print("✓ Patterns loaded successfully")
print(f"Patterns count after load: {len(p3.patterns)}")
else:
print("✗ Failed to load patterns")
except Exception as e:
print(f"✗ Exception loading patterns: {e}")
# Restore original patterns
p2.patterns = original_patterns
p3.patterns = original_patterns
# Test 5: Verify patterns object state
print("\nTest 5: Verify patterns object state")
print(f"Patterns object type: {type(p1)}")
print(f"Has save method: {hasattr(p1, 'save')}")
print(f"Has load method: {hasattr(p1, 'load')}")
print(f"PATTERNS_FILE: {p1.PATTERNS_FILE}")
# Test 6: Test pattern selection persists
print("\nTest 6: Test pattern selection")
test_pattern = "rainbow"
if test_pattern in p1.patterns:
p1.select(test_pattern)
print(f"Selected pattern: {p1.selected}")
if p1.selected == test_pattern:
print("✓ Pattern selection works")
else:
print(f"✗ Pattern selection failed. Expected '{test_pattern}', got '{p1.selected}'")
else:
print(f"Pattern '{test_pattern}' not available")
print("\n=== Patterns Save/Load test complete ===")
if __name__ == "__main__":
asyncio.run(test_patterns_save_load())

View File

@@ -1,111 +0,0 @@
#!/usr/bin/env python3
"""
Test for saving and loading settings
Run with: mpremote run test/test_save_load.py
"""
import json
import os
from settings import Settings
from patterns import Patterns
def test_save_load():
"""Test saving and loading settings"""
print("Testing save and load functionality...")
# Test 1: Save settings
print("\nTest 1: Save settings")
settings1 = Settings()
# Modify some settings
original_num_leds = settings1.get("num_leds", 50)
original_pattern = settings1.get("pattern", "off")
original_brightness = settings1.get("brightness", 127)
settings1["num_leds"] = 100
settings1["pattern"] = "rainbow"
settings1["brightness"] = 200
settings1["delay"] = 150
settings1["color1"] = "#ff0000"
settings1["color2"] = "#00ff00"
print(f"Original num_leds: {original_num_leds}")
print(f"Setting num_leds to: {settings1['num_leds']}")
print(f"Setting pattern to: {settings1['pattern']}")
print(f"Setting brightness to: {settings1['brightness']}")
# Save settings
settings1.save()
print("Settings saved")
# Test 2: Load settings
print("\nTest 2: Load settings")
settings2 = Settings()
# Verify loaded values
print(f"Loaded num_leds: {settings2['num_leds']}")
print(f"Loaded pattern: {settings2['pattern']}")
print(f"Loaded brightness: {settings2['brightness']}")
print(f"Loaded delay: {settings2.get('delay', 'not set')}")
print(f"Loaded color1: {settings2.get('color1', 'not set')}")
print(f"Loaded color2: {settings2.get('color2', 'not set')}")
# Verify values match
if settings2["num_leds"] == 100:
print("✓ num_leds saved and loaded correctly")
else:
print(f"✗ num_leds mismatch! Expected 100, got {settings2['num_leds']}")
if settings2["pattern"] == "rainbow":
print("✓ pattern saved and loaded correctly")
else:
print(f"✗ pattern mismatch! Expected 'rainbow', got '{settings2['pattern']}'")
if settings2["brightness"] == 200:
print("✓ brightness saved and loaded correctly")
else:
print(f"✗ brightness mismatch! Expected 200, got {settings2['brightness']}")
# Test 3: Test with patterns
print("\nTest 3: Test pattern persistence")
pin = settings2.get("led_pin", 10)
num_leds = settings2["num_leds"]
patterns = Patterns(pin=pin, num_leds=num_leds, selected=settings2["pattern"])
patterns.set_brightness(settings2["brightness"])
patterns.set_delay(settings2["delay"])
print(f"Pattern selected: {patterns.selected}")
print(f"Pattern brightness: {patterns.brightness}")
print(f"Pattern delay: {patterns.delay}")
if patterns.selected == settings2["pattern"]:
print("✓ Pattern selection persisted")
else:
print(f"✗ Pattern mismatch! Expected '{settings2['pattern']}', got '{patterns.selected}'")
# Test 4: Restore original settings
print("\nTest 4: Restore original settings")
settings3 = Settings()
settings3["num_leds"] = original_num_leds
settings3["pattern"] = original_pattern
settings3["brightness"] = original_brightness
settings3.save()
print(f"Restored num_leds to: {original_num_leds}")
print(f"Restored pattern to: {original_pattern}")
print(f"Restored brightness to: {original_brightness}")
# Verify restoration
settings4 = Settings()
if settings4["num_leds"] == original_num_leds:
print("✓ Settings restored correctly")
else:
print(f"✗ Restoration failed! Expected {original_num_leds}, got {settings4['num_leds']}")
print("\n=== Save/Load test complete ===")
if __name__ == "__main__":
test_save_load()