{"id":14971661,"url":"https://github.com/pimatic/rfcontroljs","last_synced_at":"2025-07-27T04:07:37.466Z","repository":{"id":19180636,"uuid":"22413227","full_name":"pimatic/rfcontroljs","owner":"pimatic","description":"nodejs modul with protocol support for different 433mhz switches and weather stations for the RFControl Arduino library.","archived":false,"fork":false,"pushed_at":"2022-08-26T19:00:25.000Z","size":766,"stargazers_count":49,"open_issues_count":12,"forks_count":51,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-06-23T13:52:28.971Z","etag":null,"topics":["433mhz","arduino","ook"],"latest_commit_sha":null,"homepage":null,"language":"CoffeeScript","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/pimatic.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-30T06:52:31.000Z","updated_at":"2024-03-07T06:23:22.000Z","dependencies_parsed_at":"2022-09-03T21:31:15.706Z","dependency_job_id":null,"html_url":"https://github.com/pimatic/rfcontroljs","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pimatic/rfcontroljs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pimatic%2Frfcontroljs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pimatic%2Frfcontroljs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pimatic%2Frfcontroljs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pimatic%2Frfcontroljs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pimatic","download_url":"https://codeload.github.com/pimatic/rfcontroljs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pimatic%2Frfcontroljs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267298755,"owners_count":24065888,"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-07-27T02:00:11.917Z","response_time":82,"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":["433mhz","arduino","ook"],"created_at":"2024-09-24T13:45:37.489Z","updated_at":"2025-07-27T04:07:37.450Z","avatar_url":"https://github.com/pimatic.png","language":"CoffeeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"rfcontroljs\n===========\n\nrfcontroljs is a node.js module written to parse and construct 433mhz On-off keying (OOK)\nsignals for various devices switches or weather stations. \n\nIt works well together with the [RFControl](https://github.com/pimatic/RFControl) Arduino library\nfor receiving the signals.\n\nYou can find a list of all supported protocols [here](protocols.md).\n\nThe Processing Pipeline\n-----------------------\n\n### 1. Receiving\n\nThe arduino is connected via serial bus to the processing computer (for example a raspberry pi)\nand waits for rf signal. \n\n\u003e Mostly all 433mhzw OOK signals from devices are send multiple times directly in row and have a\n\u003e longer footer pulse in between. They differ by the pulse lengths used to encode the data and footer \n\u003e and the pulse count.\n\n[RFControl](https://github.com/pimatic/RFControl) running on the arduino detects the start of a \nsignal by its longer footer pulse and verifies it one time by comparing it with the next signal. \nIt it unaware of the specific protocol, it just uses the stated fact above. Also we are \nnot interested in it if the pulse was a high or low pulse (presence or absence of a carrier wave), \nbecause the information is decoded in the pulse lengths.\n\nWe will call the received sequence of pulse lengths now **timings sequence**. For example a timing\nsequence in microseconds could look like this:\n\n```\n288  972  292  968  292  972  292  968  292  972  920  344  288  976  920  348  \n284  976  288  976  284  976  288  976  288  976  916  348  284  980  916  348  \n284  976  920  348  284  976  920  348  284  980  280  980  284  980  916  348  \n284  9808\n```\n\nYou can clearly see the two different pulse lengths (around 304 and 959 microseconds) for the data\nencoding and the longer footer pulse (9808 microseconds). \n\nAll observed protocols have less than 8 different pulse length and all pulse length do differ by at \nleast a factor of 2. This makes a further compression and simplification possible: We map each \npulse length to a number from 0 to 7 (a bucket) and calculate for a better accuracy the average of \nall timings mapped to each of the bucket. The result is something like that:\n\n```\nbuckets: 304, 959, 9808\npulses: 01010101011001100101010101100110011001100101011002\n```\n\nTo make the representation unique, we choose the buckets in ascending order (respectively we are\nsorting it after receiving from the arduino).\n\nWe call the sorted buckets **pulse lengths**, the compressed timings **pulse sequence** and the \nlength of the pulse sequence (inclusive footer) **pulse count**.\n\n### 2. Protocol Matching\n\nWe detect possible protocols by two criteria. The pulse length must match with a small tolerance\nand the pulse count must match. \n\n### 3. Protocol Parsing\n\nIf a protocol matches, its `parse` function is called with the pulse sequence. Most protocols are\nparsed almost the same way. First the pulse squence must be converted to a binary representation.\n\nIn almost all cases there exist a mapping from pulse sequences to a binary `0` and `1`. In this\nexample the pulse sequence `0110` represents a binary `0` and `0101` maps to a binary `1`:\n\n```CoffeeScript\npulsesToBinaryMapping = {\n  '0110': '0' #binary 0\n  '0101': '1' #binary 1 \n  '02': ''    #footer\n}\nbinary = helper.map(pulses, pulsesToBinaryMapping)\n```\n\nThe binary reprsentation now looks like this:\n\n```\n110011000010\n```\n\nAs last step the protocol dependent information must be extracted from the binary representation:\n\n```CoffeeScript\nresult = {\n  houseCode: helper.binaryToNumber(binary, 0, 5)\n  unitCode: helper.binaryToNumber(binary, 6, 10)\n  state: helper.binaryToBoolean(binary, 12)\n}\n```\n\n\nDetails\n--------\n\nRFControl is more sensitive than needed for most protocols. \nSo we get sometimes, depending of the accuracy of the sender/remote, different bucket counts. \n\nThis is by design, to catch up further protocols that maybe need a higher sensitivity. The specific\nprotocol has not to deal with this issue, because `rfcontroljs` auto merges similar buckets before\ncalling the `decodePulses` function of each protocol.\n\nThe algorithm is the following:\n\n  1. Record the (maybe to many) buckets and compressed pulses with [RFControl](https://github.com/pimatic/RFControl) (arduino / c++)\n  2. Sort the buckets in `rfcontroljs` [`prepareCompressedPulses`](https://github.com/pimatic/rfcontroljs/blob/f39db799ae1fc86cda74c33a01c27da40eb3c9e8/src/controller.coffee#L68)\n  3. Try to find a matching protocol in rfcontroljs [`decodePulses`](https://github.com/pimatic/rfcontroljs/blob/f39db799ae1fc86cda74c33a01c27da40eb3c9e8/src/controller.coffee#L118)\n  4. If we have more than 3 buckets and two of the buckets are similar (`b1*2 \u003c b2`) we merge them to just one bucket by averaging and adapting the pulses in  rfcontroljs [`fixPulses`](https://github.com/pimatic/rfcontroljs/blob/f39db799ae1fc86cda74c33a01c27da40eb3c9e8/src/controller.coffee#L89)\n  5. Go to step 3\n\nAdding a new Protocol\n--------------------\n\n## Preparation\n\n1. Fork the rfcontroljs repository and clone your fork into a local directory.\n2. run `npm install` inside the cloned directory, so that all dependencies get installed.\n3. We are using [gulp](http://gulpjs.com/) for automating tests and automatic coffee-script compilation. So best to install it global: `npm install --global gulp`\n4. You should be able to run the tests with `gulp test`.\n5. Running just `gulp` let it compile all files and whats for changes. So always keep in running while editing coffee-files.\n\n## Protocol development\n\n1. Create a new protocol file (like the other) in `src/protocols`.\n2. Add its name in the `src/controller.coffee` file to [the protocol list](https://github.com/pimatic/rfcontroljs/blob/master/src/controller.coffee#L2).\n3. Add a test case to the `#decodePulses()` test case list in `test/lib-controller.coffee` with the data from the arduino ([like this one](https://github.com/pimatic/rfcontroljs/blob/master/test/lib-controller.coffee#L65-L74)). For the `pulseLengths`: strip the zero's at the end and sort them in ascending order, also adapt the `pulse` to the changed sorting. [1]\n4. Adapt the protocol file, so that the test get passed.\n\n[1] You can also use this script to convert the output to a valid test input:\n\n```coffee\ncontroller = require './index.js'\nresult = controller.prepareCompressedPulses('255 2904 1388 771 11346 0 0 0 0100020002020000020002020000020002000202000200020002000200000202000200020000020002000200020002020002000002000200000002000200020002020002000200020034')\nconsole.log result\nresult2 = controller.fixPulses(result.pulseLengths, result.pulses)\nconsole.log result2\n```\nsample output:\n\n```\ncoffee convert.coffee \n{ pulseLengths: [ 255, 771, 1388, 2904, 11346 ],\n  pulses: '0300020002020000020002020000020002000202000200020002000200000202000200020000020002000200020002020002000002000200000002000200020002020002000200020014' }\n{ pulseLengths: [ 255, 1079, 2904, 11346 ],\n  pulses: '0200010001010000010001010000010001000101000100010001000100000101000100010000010001000100010001010001000001000100000001000100010001010001000100010013' }\n```\nThe second line should be used for protocol developing.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpimatic%2Frfcontroljs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpimatic%2Frfcontroljs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpimatic%2Frfcontroljs/lists"}