{"id":19294468,"url":"https://github.com/plummerssoftwarellc/nightdriverserver","last_synced_at":"2025-09-03T04:38:03.215Z","repository":{"id":107484538,"uuid":"290263346","full_name":"PlummersSoftwareLLC/NightDriverServer","owner":"PlummersSoftwareLLC","description":"A C# demo application for sending color data over WiFi to an ESP32 NightDriverStrip","archived":false,"fork":false,"pushed_at":"2023-12-28T06:21:08.000Z","size":233,"stargazers_count":23,"open_issues_count":1,"forks_count":4,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-08-31T09:53:22.732Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PlummersSoftwareLLC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-08-25T16:15:04.000Z","updated_at":"2025-07-27T06:24:22.000Z","dependencies_parsed_at":"2023-12-16T19:23:00.684Z","dependency_job_id":null,"html_url":"https://github.com/PlummersSoftwareLLC/NightDriverServer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/PlummersSoftwareLLC/NightDriverServer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FNightDriverServer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FNightDriverServer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FNightDriverServer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FNightDriverServer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PlummersSoftwareLLC","download_url":"https://codeload.github.com/PlummersSoftwareLLC/NightDriverServer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlummersSoftwareLLC%2FNightDriverServer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273392280,"owners_count":25097256,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T22:38:30.761Z","updated_at":"2025-09-03T04:38:03.195Z","avatar_url":"https://github.com/PlummersSoftwareLLC.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NightDriverServer\n\n## Purpose\n\nThe purpose of the NightDriverServer project is to serve as a \"demo\" of how to send color data over WiFi to a NightDriverStrip instance on an ESP32.  You must have the equivalent of the \"LEDSTRIP\" proejct running with wifi and incoming data enabled (which LEDSTRIP does by default).  When run, the ESP32 will connect to WiFi and wait for TCP connections on port 49152.\n\nWhile this demo is in C#, any lanugage that can create a byte array and send it to a socket will work.  I've done examples in C++, Python, and C# in the past.\n\n## Packet Format\n\nThe format for a packet of color data, a single frame, is:\n\nThe function LightStrip::GetDataFrame is the one that puts together the packet and returns a byte array to be sent to the socket.\n\n```csharp\n    var data = GetPixelData(MainLEDs);\n    return\n        LEDInterop.CombineByteArrays(LEDInterop.WORDToBytes(WIFI_COMMAND_PIXELDATA64), \n            LEDInterop.WORDToBytes((UInt16)Channel), // LED channel on ESP32\n            LEDInterop.DWORDToBytes((UInt32)data.Length / 3), // Number of LEDs\n            LEDInterop.ULONGToBytes(seconds), // Timestamp seconds (64 bit)\n            LEDInterop.ULONGToBytes(uSeconds), // Timestmap microseconds (64 bit)\n            data); // Color Data\n```\n\nThe packet format, then, starts with a WORD magic cookie of WIFI_COMMAND_PIXELDATA64, which is 3.  \nThe next WORD is the LED channel (or 0 for 'all channels'.)\nThe next DWORD is the number of LEDs being set\nThe next ULONG (64 bits) is the timestamp (whole seconds)\nThe next ULONG (64 bits) is the timestamp (microseconds)\nThen the raw color data in RGB format (one 3-byte RGB triplet per LED being sent)\n\nThis demo sets the timestamp to be a second or two in the future, depending on how much RAM the NightDriverStrip has, so that each strip can hold its data until the timestamp comes due and then they all show it at the same time, which is how multiple strips stay in sync.\n\n## Compressed Packets\n\nThis data may optionally be compressed.  In the function CompressFame, you can see where the above packet is wrapped in a compression packet. The format of a compressed packet is:\n\nFirst DWORD is the header tag:  0x44415645  (just a magic value)\nSecond DWORD is compressed data length in bytes\nThird DWORD is the original data length in bytes\nFourth DWORD is unused, set to 0x12345678 for now\nThen the bytes of the original color data packet in LZCompressed format\n\nYou can see how the original packet is compressed in the Compress function, which uses a ZLIBStream to make the lzcompressed data in a manner that can be decompressed by the ESP32 that is receiving the data.\n\n## How the Server Works\n\nThe program creates an array of Location objects.  A Location implements the GraphicsBase interface so you can call GetPixel and SetPixel and so on.\n\nThe main function here is DrawAndEnqueueAll, which figures out which effect is currently running.  It then calls that effect's DrawFrame call which in turn will draw into the Location.  Once the drawing is complete it calls CompressAndEnqueueData on the controller object, which will put the data into a queue, one per location.\n\nEach Location has a peer thread running an LEDControllerChannel, and it is that controller that talks directly to the socket on the strip, sending a packer every frame.\n\n## How to Cheat and Get Started\n\nNone of this complexity is needed uness you have many locations.  If you were just sending to a single strip, you could simply write a loop that sent frames to the strip 20 times per second.  If you set the timestamps to 0, it should draw that frame immediately instead of buffering it.  That's the quickest way to get something running in a new lanugage or project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplummerssoftwarellc%2Fnightdriverserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplummerssoftwarellc%2Fnightdriverserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplummerssoftwarellc%2Fnightdriverserver/lists"}