├── .gitignore ├── .gitmodules ├── CREDITS.md ├── HACKING.md ├── INSTALL.md ├── LICENSES.md ├── README.md ├── TODO.md ├── bin ├── nrf51-uart-bridge.hex └── tx.hex ├── configurator ├── .gitignore ├── common-modules │ ├── configurator-ws-server │ │ ├── LICENSE │ │ ├── index.js │ │ └── package.json │ └── test-data │ │ ├── LICENSE │ │ ├── index.js │ │ └── package.json ├── config-backups │ ├── README.md │ ├── headless-tx-backup-20170218T1037.json │ ├── headless-tx-backup-20170218T1339-new-tx-uuids.json │ ├── headless-tx-backup-20170519T1937.json │ ├── headless-tx-backup-20170521T1717.json │ ├── headless-tx-backup-20170524T2010.json │ ├── headless-tx-backup-20170715T1348.json │ ├── headless-tx-backup-20180819T1334.json │ ├── headless-tx-backup-20180902T1649.json │ ├── headless-tx-backup-20181028T2055.json │ ├── headless-tx-backup-20181220T1814.json │ ├── headless-tx-backup-20181226T1821.json │ ├── headless-tx-backup-20190417T1704.json │ ├── headless-tx-backup-20190915T1825.json │ ├── headless-tx-backup-20191026T1339.json │ ├── headless-tx-backup-20200627T1351.json │ └── headless-tx-backup-20201223T1506.json ├── nodejs-headlesstx-simulator-websocket │ ├── .eslintrc.js │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── simulator.js │ └── test.html ├── nodejs-uart-websocket-bridge │ ├── LICENSE │ ├── README.md │ ├── bridge.js │ ├── package.json │ └── test.html ├── nrf51-nrf-uart-bridge │ ├── LICENSE │ ├── README.md │ └── firmware │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── linker.ld │ │ ├── main.c │ │ ├── nrf_drv_config.h │ │ ├── rf_protocol.c │ │ ├── rf_protocol.h │ │ ├── slip.c │ │ └── slip.h ├── orangepizero │ ├── HARDWARE.md │ ├── INSTALL.md │ ├── README.md │ ├── configurator-pcb1.jpg │ ├── etc │ │ ├── apt │ │ │ └── apt.conf.d │ │ │ │ └── 90no-auto-upgrades │ │ ├── dnsmasq.d │ │ │ └── configurator │ │ ├── hostapd.configurator.conf │ │ ├── network │ │ │ └── interfaces.hostapd-configurator │ │ ├── nginx │ │ │ └── sites-available │ │ │ │ └── configurator │ │ ├── overlayroot.local.conf │ │ └── udev │ │ │ └── rules.d │ │ │ └── 99-gpio.rules │ ├── install-dependencies │ ├── mechanics │ │ ├── configurator-label.pdf │ │ ├── configurator-label.svg │ │ └── orangepizero-configurator-case.scad │ ├── openocd │ │ ├── README.md │ │ ├── flash-nrf51 │ │ ├── flash-stm32f103c8t6 │ │ ├── orangepizero-nrf51.cfg │ │ └── orangepizero-stm32f1.cfg │ ├── shutdown-on-button-press.py │ └── var │ │ └── www │ │ └── html │ │ └── index.configurator.html ├── usb-dongle │ ├── electronics │ │ ├── .gitignore │ │ ├── rescue-backup │ │ │ ├── usb-dongle-2018-08-21-16-41-39.pro │ │ │ ├── usb-dongle-2018-08-21-16-41-39.sch │ │ │ ├── usb-dongle-cache-2018-08-21-16-41-39.lib │ │ │ └── usb-dongle-rescue-2018-08-21-16-41-39.lib │ │ ├── sym-lib-table │ │ ├── usb-dongle-cache.lib │ │ ├── usb-dongle-rescue.lib │ │ ├── usb-dongle.kicad_pcb │ │ ├── usb-dongle.net │ │ ├── usb-dongle.pro │ │ └── usb-dongle.sch │ ├── firmware │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── gdb.flash.script │ │ ├── main.c │ │ ├── stm32f103c8t6.ld │ │ ├── system │ │ │ ├── error_handler.c │ │ │ ├── led.c │ │ │ ├── led.h │ │ │ ├── serial_number.c │ │ │ ├── serial_number.h │ │ │ ├── systick.c │ │ │ ├── systick.h │ │ │ ├── uart.c │ │ │ ├── uart.h │ │ │ ├── watchdog.c │ │ │ └── watchdog.h │ │ ├── usb │ │ │ ├── webusb.c │ │ │ └── webusb.h │ │ └── utils │ │ │ ├── ring_buffer.c │ │ │ └── ring_buffer.h │ └── webusb │ │ ├── index.html │ │ └── webusb.js └── web-app │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── app │ ├── .eslintrc.js │ ├── fonts │ │ ├── LICENSE.txt │ │ ├── MaterialIcons-Regular.eot │ │ ├── MaterialIcons-Regular.ttf │ │ ├── MaterialIcons-Regular.woff │ │ ├── MaterialIcons-Regular.woff2 │ │ └── materialicons.css │ ├── html │ │ ├── app.html │ │ ├── index.html │ │ └── pages │ │ │ ├── about.html │ │ │ ├── device_list.html │ │ │ ├── dialogs.html │ │ │ ├── edit_curve.html │ │ │ ├── edit_switch.html │ │ │ ├── hardware_inputs.html │ │ │ ├── hardware_inputs_order.html │ │ │ ├── limits.html │ │ │ ├── logical_inputs.html │ │ │ ├── main.html │ │ │ ├── mixer.html │ │ │ ├── mixer_unit.html │ │ │ ├── model_details.html │ │ │ ├── model_list.html │ │ │ ├── rf_protocol.html │ │ │ ├── select_icon.html │ │ │ ├── select_multiple.html │ │ │ ├── select_single.html │ │ │ ├── settings.html │ │ │ ├── transmitter_details.html │ │ │ └── transmitter_list.html │ ├── images │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── laneboysrc-banner.png │ │ ├── laneboysrc-blog.jpg │ │ ├── laneboysrc-logo-128-t.png │ │ ├── laneboysrc-logo-128.jpg │ │ ├── laneboysrc-logo-144.png │ │ ├── laneboysrc-logo-180.png │ │ ├── laneboysrc-logo-192.png │ │ └── laneboysrc-yt.jpg │ ├── index.js │ ├── javascripts │ │ ├── FileSaver.min.js │ │ ├── Sortable.js │ │ ├── material.min.js │ │ ├── service-worker-registration.js │ │ └── strftime.js │ ├── modules │ │ ├── about.js │ │ ├── config.js │ │ ├── curve_view.js │ │ ├── database.js │ │ ├── database_object.js │ │ ├── device.js │ │ ├── device_list.js │ │ ├── edit_curve.js │ │ ├── edit_switch.js │ │ ├── hardware_inputs.js │ │ ├── hardware_inputs_order.js │ │ ├── index.js │ │ ├── limits.js │ │ ├── logical_inputs.js │ │ ├── main.js │ │ ├── mdl_helper.js │ │ ├── mixer.js │ │ ├── mixer_unit.js │ │ ├── model_details.js │ │ ├── model_list.js │ │ ├── path.js │ │ ├── rf_protocol.js │ │ ├── routes.js │ │ ├── select_icon.js │ │ ├── select_multiple.js │ │ ├── select_single.js │ │ ├── settings.js │ │ ├── slip.js │ │ ├── test_database.js │ │ ├── transmitter_details.js │ │ ├── transmitter_list.js │ │ ├── utils.js │ │ ├── websocket_transport.js │ │ └── webusb_transport.js │ ├── static │ │ └── manifest.json │ └── stylesheets │ │ ├── dialog-polyfill.css │ │ ├── material.deep_orange-blue.min.configurator.css │ │ ├── material.deep_orange-blue.min.css │ │ └── styles.css │ ├── package.json │ ├── webpack.config.js │ └── webpack.support.js ├── docs ├── architecture.md ├── battery-life-test │ ├── .gitignore │ ├── 2016-06-04_Sony800mAh-3.63V-to-shutoff.csv │ ├── 2016-06-04_Sony800mAh-3.63V-to-shutoff.ods │ ├── 2016-06-04_Sony800mAh-3.63V-to-shutoff.pdf │ ├── 2016-06-05-Sony800mAh-full-to-shutoff.csv │ ├── 2016-06-05-Sony800mAh-full-to-shutoff.ods │ ├── 2016-06-05-Sony800mAh-full-to-shutoff.pdf │ ├── battery-life-test-keithley-2700.py │ └── scpi │ │ ├── README.md │ │ ├── __init__.py │ │ ├── devices │ │ ├── __init__.py │ │ └── hp6632b.py │ │ ├── errors │ │ └── __init__.py │ │ ├── scpi.py │ │ ├── serial_monitor.py │ │ └── transports │ │ ├── __init__.py │ │ ├── baseclass.py │ │ └── rs232.py ├── configurator-sequence-diagram.odg ├── configurator-sequence-diagram.pdf ├── configurator-ui-flow.odg ├── configurator-ui-flow.png ├── configurator.md ├── new-rf-protocol.md ├── transmitter-configurator-phone.jpg └── transmitter-hardware-overview.ods ├── rc-headless-transmitter.sublime-project └── transmitter ├── electronics ├── .gitignore ├── BOM.txt ├── README.md ├── WLA.pretty │ ├── LiPo_charger_and_protection.kicad_mod │ └── STM32F103C8T6_DEV_BOARD.kicad_mod ├── airtronics-meter.md ├── airtronics │ ├── fp-info-cache │ ├── stm32f1-nrf24l01-transmitter-airtronics.kicad_pcb │ ├── stm32f1-nrf24l01-transmitter-airtronics.pro │ ├── stm32f1-nrf24l01-transmitter-airtronics.sch │ ├── stm32f1-nrf24l01-transmitter-rescue.lib │ └── sym-lib-table ├── futaba-meter.md ├── heathkit-pcb-connection-diagram.pdf ├── pcb │ ├── .gitignore │ ├── fp-info-cache │ ├── fp-lib-table │ ├── measurements.txt │ ├── rc-headless-tx-gerber.zip │ ├── stm32f1-nrf24l01-transmitter-pcb-cache.lib │ ├── stm32f1-nrf24l01-transmitter-pcb-rescue.lib │ ├── stm32f1-nrf24l01-transmitter-pcb.kicad_pcb │ ├── stm32f1-nrf24l01-transmitter-pcb.pdf │ ├── stm32f1-nrf24l01-transmitter-pcb.pro │ ├── stm32f1-nrf24l01-transmitter-pcb.sch │ ├── sym-lib-table │ └── tact-switch.pretty │ │ └── Tact switch 6x4x3.kicad_mod ├── prototyping-board-nrf-on-top │ ├── fp-info-cache │ ├── stm32f1-nrf24l01-transmitter-nrf_on_top.kicad_pcb │ ├── stm32f1-nrf24l01-transmitter-nrf_on_top.pro │ ├── stm32f1-nrf24l01-transmitter-rescue.lib │ ├── stm32f1-nrf24l01-transmitter.sch │ └── sym-lib-table ├── prototyping-board │ ├── fp-info-cache │ ├── fp-lib-table │ ├── stm32f1-nrf24l01-transmitter-rescue.lib │ ├── stm32f1-nrf24l01-transmitter.kicad_pcb │ ├── stm32f1-nrf24l01-transmitter.pro │ ├── stm32f1-nrf24l01-transmitter.sch │ └── sym-lib-table ├── stm32f1-nrf24l01-transmitter.pdf └── world-engines │ ├── fp-info-cache │ ├── stm32f1-nrf24l01-transmitter-rescue.lib │ ├── stm32f1-nrf24l01-transmitter-world-engines.kicad_pcb │ ├── stm32f1-nrf24l01-transmitter-world-engines.pro │ ├── stm32f1-nrf24l01-transmitter-world-engines.sch │ └── sym-lib-table ├── firmware ├── .gitignore ├── INO-FILE-COMPILATION.md ├── LICENSE ├── Makefile ├── README.md ├── configuration │ ├── config.c │ ├── config.h │ ├── config_to_javascript.c │ ├── configurator.c │ └── configurator.h ├── gdb.flash.script ├── input │ ├── inputs.c │ ├── inputs.h │ ├── inputs_stm32f103c8t6.c │ └── inputs_stm32f103c8t6.h ├── main.c ├── mixer │ ├── channels.c │ ├── channels.h │ ├── curves.c │ ├── curves.h │ ├── limits.c │ ├── limits.h │ ├── mixer.c │ └── mixer.h ├── rf │ ├── nrf24l01p.c │ ├── nrf24l01p.h │ ├── protocol_hk310.c │ ├── protocol_hk310.h │ ├── protocol_laneboysrc4ch.c │ ├── protocol_laneboysrc4ch.h │ ├── protocol_laneboysrc8ch.c │ └── protocol_laneboysrc8ch.h ├── sound │ ├── music.c │ ├── music.h │ ├── sound.c │ └── sound.h ├── stm32f103c8t6.ld ├── system │ ├── battery.c │ ├── battery.h │ ├── error_handler.c │ ├── led.c │ ├── led.h │ ├── meter.c │ ├── meter.h │ ├── persistent_storage.c │ ├── persistent_storage.h │ ├── serial_number.c │ ├── serial_number.h │ ├── spi.c │ ├── spi.h │ ├── systick.c │ ├── systick.h │ ├── uart.c │ ├── uart.h │ ├── watchdog.c │ └── watchdog.h ├── usb │ ├── webusb.c │ ├── webusb.h │ └── webusb_bos.h └── utils │ ├── ring_buffer.c │ ├── ring_buffer.h │ ├── slip.c │ └── slip.h ├── mechanics ├── devo4-antenna-guide.scad ├── devo4-switch-mount.scad ├── esp8266-nrf51-configurator-case.png ├── esp8266-nrf51-configurator-case.scad ├── esp8266-nrf51-configurator-label.svg ├── graupner-e8-antenna-guide.scad ├── graupner-e8-push-button-mount.scad ├── graupner-e8-switch-plate.scad └── graupner-e8-usb-mount.scad └── windows7-driver ├── amd64 ├── WUDFUpdate_01009.dll ├── WdfCoInstaller01009.dll └── winusbcoinstaller2.dll ├── rc-headless-tx.inf └── x86 ├── WUDFUpdate_01009.dll ├── WdfCoInstaller01009.dll └── winusbcoinstaller2.dll /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-workspace 2 | gh-pages/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "firmware/libs/libopencm3"] 2 | path = transmitter/firmware/libs/libopencm3 3 | url = https://github.com/libopencm3/libopencm3 4 | -------------------------------------------------------------------------------- /HACKING.md: -------------------------------------------------------------------------------- 1 | This project uses different type of microcontrollers: 2 | * [STM32F103](http://www.st.com/content/st_com/en/products/microcontrollers/stm32-32-bit-arm-cortex-mcus/stm32f1-series/stm32f103.html?querycriteria=productId=LN1565) 3 | * [Nordic nRF51822](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF51822) 4 | 5 | On the software side, you need to be familiar with [Git](https://git-scm.com/), the [ARM GCC toolchain](https://launchpad.net/gcc-arm-embedded), [GNU Make](https://www.gnu.org/software/make/), [OpenOCD](http://openocd.org/) and the [ST-Link programmer](http://www.st.com/content/st_com/en/products/development-tools/hardware-development-tools/development-tool-hardware-for-mcus/debug-hardware-for-mcus/debug-hardware-for-stm32-mcus/st-link-v2.html). 6 | 7 | If you are on Windows it may be easier to compile the software with [Git Bash](https://git-for-windows.github.io/) shell. 8 | 9 | The web interface and associated dev tools are built in JavaScript using [Node.js](https://nodejs.org/), [Webpack](http://webpack.github.io/) and the [Material Design Lite framework](https://getmdl.io/). 10 | 11 | **IMPORTANT** 12 | This project makes use of Git submodules. Please clone it by running: 13 | 14 | git clone --recursive https://github.com/laneboysrc/rc-headless-transmitter.git 15 | 16 | Alternatively, run the following commands in the project root: 17 | 18 | git submodule init 19 | git submodule update 20 | 21 | 22 | --- 23 | 24 | # GH-PAGES 25 | 26 | We can host the web-app via Github pages conveniently. Since it is served via HTTPS we can use ServiceWorker and other goodies. 27 | 28 | ## Setup 29 | 30 | In the root of the project, execute 31 | 32 | git clone https://github.com/laneboysrc/rc-headless-transmitter.git --branch gh-pages --single-branch gh-pages 33 | 34 | This creates a folder named `gh-pages` with the web-app source code, cloned from the current state of the rc-headless-transmitter's `gh-pages` branch. Note that the `gh-pages` is in `.gitignore` of the `master` branch, so it does not appear in the `master` branch. 35 | 36 | When building the web-app (run `npm run build` in `configurator/web-app`), the files are automatically placed into the `gh-pages` folder. 37 | 38 | To update the web-app on Github, go into the `gh-pages` folder, commit the changes, and run `git push`. 39 | 40 | 41 | Source: 42 | [http://stackoverflow.com/questions/1778088/how-to-clone-a-single-branch-in-git](http://stackoverflow.com/questions/1778088/how-to-clone-a-single-branch-in-git) 43 | 44 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # You can do it! 2 | 3 | Don't be discouraged: One can learn *anything* with the help of the Internet today. It is only up to *you* to invest the time. 4 | 5 | **A day where you learn something new is a good day.** 6 | 7 | --- 8 | 9 | # How to get up and running 10 | 11 | This project requires **basic** skills in electronics, soldering and Linux (Raspberry Pi level, e.g. using ssh, networking, running commands, editing files). While the electronics are based on off-the-shelf modules, everything needs to be wired correctly on a prototyping board and installed in the transmitter. 12 | 13 | The *headless transmitter* comprises of an inexpensive ["Blue pill" board](https://www.aliexpress.com/item/1pcs-STM32F103C8T6-ARM-STM32-Minimum-System-Development-Board-Module-For-arduino/32478120209.html) with an STM32F103 micro-controller. It drives a nRF24L01+ module with integrated power amplifier. A single cell Li-Ion battery, protected and charged by another cheap module, is used as power source. There is no need to make a PCB, everything fits easily on a prototyping board. 14 | Please refer to [transmitter/electronics/BOM.txt](transmitter/electronics/BOM.txt) for a full list of components required. 15 | 16 | The *configurator* is built with an [Orange Pi Zero](https://www.aliexpress.com/store/product/New-Orange-Pi-Zero-H2-Quad-Core-Open-source-development-board-beyond-Raspberry-Pi/1553371_32760774493.html) and a CORE51822B module. The 256 MByte version of the Orange Pi Zero is enough for our simple application. It is powered by a suitable power bank or other 5V supply. 17 | Please refer to [configurator/orangepizero/HARDWARE.md](configurator/orangepizero/HARDWARE.md) for a full list of components required. 18 | 19 | Follow the instructions in [configurator/orangepizero/INSTALL.md](configurator/orangepizero/INSTALL.md) to install and configure the Armbian Linux distribution and required software on the Orange Pi Zero. 20 | 21 | The firmware for the micro-controllers comes pre-compiled in the [bin/](bin/) folder of the project, no need to install development tools and compile it yourself. You can use the Orange Pi Zero to flash it. Refer to [configurator/orangepizero/openocd/README.md](configurator/orangepizero/openocd/README.md) for instructions. 22 | 23 | 24 | # First time use 25 | 26 | Power up the *headless transmitter*. You should hear a short beep and the LED should be on. The transmitter is now operational, with a simple default configuration. 27 | 28 | Turn on the *configurator* and wait until it has booted (green LED turns solid). 29 | 30 | On your PC or Smartphone, open [https://laneboysrc.github.io/rc-headless-transmitter](https://laneboysrc.github.io/rc-headless-transmitter) in the web browser. 31 | 32 | Now connect Wi-Fi to the SSID of the *configurator*, which is "LANE Boys RC". Use the passphrase you have configured when building the *configurator* (default is "12345678", but you hopefully changed that while building your own *configurator* ...). 33 | 34 | After pressing the `Connect` button on the web-app, the list of transmitters should show `Unconfigured Tx`. Click on the `Edit` icon next to the name. The transmitter should beep and after a few seconds the web-app will show the settings of the transmitter. You can now proceed with configuring it. 35 | 36 | After connecting to a transmitter for the first time, you should give it a unique name and assign a new RF protocol address and hop sequence. 37 | 38 | -------------------------------------------------------------------------------- /LICENSES.md: -------------------------------------------------------------------------------- 1 | Different components of this project are released under different licenses. 2 | Please refer to files named LICENSE in the respective sub-directories. 3 | 4 | Summary: 5 | - The transmitter firmware on the STM32F103 is GPL 3 licensed as it builds on Deviation TX. 6 | - Other software is MIT licensed. 7 | -------------------------------------------------------------------------------- /configurator/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /configurator/common-modules/configurator-ws-server/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Werner Lane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /configurator/common-modules/configurator-ws-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "configurator-ws-server", 3 | "version": "1.0.1", 4 | "description": "Headless TX simulator using Websocket protocol", 5 | "repository": "https://github.com/laneboysrc/rc-headless-transmitter.git", 6 | "author": "laneboysrc@gmail.com", 7 | "license": "MIT", 8 | "dependencies": { 9 | "nodejs-websocket": "^1.7.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /configurator/common-modules/test-data/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Werner Lane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /configurator/common-modules/test-data/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-data", 3 | "version": "1.0.0", 4 | "description": "Headless TX configuration test data", 5 | "repository": "https://github.com/laneboysrc/rc-headless-transmitter.git", 6 | "author": "laneboysrc@gmail.com", 7 | "license": "MIT" 8 | } 9 | -------------------------------------------------------------------------------- /configurator/config-backups/README.md: -------------------------------------------------------------------------------- 1 | This folder contains backups of configurations for transmitters and models 2 | owned by LANE Boys RC. They will most likely not be useful for you at all. 3 | 4 | They were created by the configurator web-app, and can be reimported there 5 | if needed. They were put here for backup purpose. 6 | 7 | 8 | 9 | ## Steering dual-rate 10 | 11 | On some transmitters we may have a switch, on others we have a potentiometer. 12 | Ideally we can use the same model configuration in both scenarios, and if neither pot nor switch is present the DR is set to 100%. Ideally the DR value for switch is 70%, and the minimum DR value for pot is 50%. 13 | 14 | The dual rate is achived by *multiplying* the CH1 value (already derived from ST) by the amount specified by a mixer that uses ST-DR as input. 15 | 16 | The following values apply to pots and switches: 17 | 18 | 19 | left/off right/on not present 20 | Pot 0 100 0 21 | SW -100 100 0 22 | 23 | The pot value is written for an pot of analog, positive only value. 24 | 25 | Given this combination, we will use 3 point curve with the following mapping: 26 | 27 | x y 28 | -100 100 29 | 0 100 30 | 100 70 31 | 32 | We can not achieve unfortunately that the maximum DR value for the switch and pot are different. -------------------------------------------------------------------------------- /configurator/nodejs-headlesstx-simulator-websocket/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Werner Lane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /configurator/nodejs-headlesstx-simulator-websocket/README.md: -------------------------------------------------------------------------------- 1 | # Headless TX simulator using the Websocket protocol 2 | 3 | This tool simulates a *headless transmitter*, connected to a bridge that implements the 4 | WebSocket interface. 5 | 6 | This tool allows testing the web-app without hardware. 7 | 8 | 9 | ## Build instructions 10 | 11 | Install NodeJS [http://nodejs.org/](http://nodejs.org/) 12 | 13 | Run `npm install` to fetch the required modules. 14 | 15 | You can then run `npm start` -------------------------------------------------------------------------------- /configurator/nodejs-headlesstx-simulator-websocket/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "headlesstx-simulator-websocket", 3 | "version": "1.0.0", 4 | "description": "Headless TX simulator using Websocket protocol", 5 | "repository": "https://github.com/laneboysrc/rc-headless-transmitter.git", 6 | "author": "laneboysrc@gmail.com", 7 | "license": "MIT", 8 | "scripts": { 9 | "start": "./simulator.js" 10 | }, 11 | "dependencies": { 12 | "configurator-ws-server": "file:///home/laneboysrc/projects/rc-headless-transmitter/configurator/common-modules/configurator-ws-server", 13 | "test-data": "file:../common-modules/test-data" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /configurator/nodejs-uart-websocket-bridge/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Werner Lane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /configurator/nodejs-uart-websocket-bridge/README.md: -------------------------------------------------------------------------------- 1 | # UART to Websocket bridge protocol 2 | 3 | This tool can be connected to a *headless transmitter* via the UART. It exposes the *headless transmitter* to a *configurator* using the WebSocket protocol. 4 | 5 | This program is used on the Orange Pi Zero to bridge the nRF51822, which in turn bridges the UART to the *headless transmitter*, to the web-app. 6 | 7 | Please refer to the [configurator](../../docs/configurator.md) documentation for more information. 8 | 9 | 10 | ## Build instructions 11 | 12 | Install NodeJS [http://nodejs.org/](http://nodejs.org/) 13 | 14 | Run `npm install` to fetch the required modules. 15 | 16 | You can then run `npm start /dev/ttyS1`. -------------------------------------------------------------------------------- /configurator/nodejs-uart-websocket-bridge/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uart-websocket-bridge", 3 | "version": "1.0.0", 4 | "description": "Bridge a Headless TX connected via a UART to a configuratior using the Websocket protocol", 5 | "repository": "https://github.com/laneboysrc/rc-headless-transmitter.git", 6 | "author": "laneboysrc@gmail.com", 7 | "license": "MIT", 8 | "scripts": { 9 | "start": "./bridge.js" 10 | }, 11 | "dependencies": { 12 | "configurator-ws-server": "file:../common-modules/configurator-ws-server", 13 | "minimist": "~1.2.0", 14 | "serialport": "~4.0.1", 15 | "slip": "~1.0.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /configurator/nodejs-uart-websocket-bridge/test.html: -------------------------------------------------------------------------------- 1 | ../nodejs-headlesstx-simulator-websocket/test.html -------------------------------------------------------------------------------- /configurator/nrf51-nrf-uart-bridge/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Werner Lane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /configurator/nrf51-nrf-uart-bridge/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the firmware that goes into the *configurator* of the [*headless transmitter*](https://github.com/laneboysrc/rc-headless-transmitter). 2 | 3 | It runs on the Nordic nRF51822 RF micro-controller. 4 | 5 | You need to download the [nRF5 SDK version 11.0](https://www.nordicsemi.com/eng/Products/Bluetooth-low-energy/nRF5-SDK) from Nordic. The SDK must reside in the `firmware/nrf51_sdk/` folder. 6 | *Note:* It is important that you use the exact version mentioned above as newer versions have ab incompatible API. 7 | 8 | The firmware is built using a [GNU Make](https://www.gnu.org/software/make/) makefile. The compiler is [ARM GCC toolchain](https://launchpad.net/gcc-arm-embedded), version 5.2.1. [OpenOCD](http://openocd.org/) is used to program the firmware into the micro-controller. You can use the ST-Link v2 to program the nRF51822. 9 | 10 | 11 | Run 12 | 13 | make 14 | 15 | to build the firmware. When done, run 16 | 17 | make program 18 | 19 | to flash the firmware into the micro-controller. 20 | 21 | -------------------------------------------------------------------------------- /configurator/nrf51-nrf-uart-bridge/firmware/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | nrf51_sdk 3 | discovery-st-link-serial.cfg 4 | -------------------------------------------------------------------------------- /configurator/nrf51-nrf-uart-bridge/firmware/linker.ld: -------------------------------------------------------------------------------- 1 | /* Linker script to configure memory regions. */ 2 | 3 | SEARCH_DIR(.) 4 | GROUP(-lgcc -lc -lnosys) 5 | 6 | MEMORY 7 | { 8 | FLASH (rx) : ORIGIN = 0x0, LENGTH = 256k 9 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16k 10 | } 11 | 12 | SECTIONS 13 | { 14 | .fs_data_out ALIGN(4): 15 | { 16 | PROVIDE( __start_fs_data = .); 17 | KEEP(*(fs_data)) 18 | PROVIDE( __stop_fs_data = .); 19 | } = 0 20 | } 21 | 22 | INCLUDE "nrf5x_common.ld" 23 | -------------------------------------------------------------------------------- /configurator/nrf51-nrf-uart-bridge/firmware/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | 13 | volatile uint32_t milliseconds; 14 | 15 | 16 | // **************************************************************************** 17 | static void rtc_callback(nrf_drv_rtc_int_type_t int_type) 18 | { 19 | // Since we only enable the tick callback we don't have to test int_type 20 | milliseconds += 1000 / RTC0_CONFIG_FREQUENCY; 21 | } 22 | 23 | void debug_printf(const char * format, ...); 24 | 25 | // **************************************************************************** 26 | static void uart_error_handler(app_uart_evt_t * p_app_uart_event) 27 | { 28 | if (p_app_uart_event->evt_type == APP_UART_COMMUNICATION_ERROR) { 29 | debug_printf("\nUART_COMM_ERR %08x\n", p_app_uart_event->data.error_communication); 30 | } 31 | else if (p_app_uart_event->evt_type == APP_UART_FIFO_ERROR) { 32 | debug_printf("UART_FIFO_ERROR"); 33 | } 34 | } 35 | 36 | 37 | // **************************************************************************** 38 | static void CLOCKS_init( void ) 39 | { 40 | // Start the 16 MHz crystal oscillator 41 | NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; 42 | NRF_CLOCK->TASKS_HFCLKSTART = 1; 43 | while (! NRF_CLOCK->EVENTS_HFCLKSTARTED); 44 | 45 | // Start low frequency crystal oscillator (used by the RTC) 46 | NRF_CLOCK->LFCLKSRC = (CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos); 47 | NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; 48 | NRF_CLOCK->TASKS_LFCLKSTART = 1; 49 | while (! NRF_CLOCK->EVENTS_LFCLKSTARTED); 50 | } 51 | 52 | 53 | // **************************************************************************** 54 | static void UART_init(void) 55 | { 56 | uint32_t err_code; 57 | const app_uart_comm_params_t comm_params = { 58 | .rx_pin_no = UART0_CONFIG_PSEL_RXD, 59 | .tx_pin_no = UART0_CONFIG_PSEL_TXD, 60 | .rts_pin_no = UART0_CONFIG_PSEL_RTS, 61 | .cts_pin_no = UART0_CONFIG_PSEL_CTS, 62 | .flow_control = UART0_CONFIG_HWFC, 63 | .use_parity = UART0_CONFIG_PARITY, 64 | .baud_rate = UART0_CONFIG_BAUDRATE 65 | }; 66 | 67 | APP_UART_FIFO_INIT(&comm_params, UART0_CONFIG_RX_BUFFER_SIZE, 68 | UART0_CONFIG_TX_BUFFER_SIZE, uart_error_handler, APP_IRQ_PRIORITY_LOW, 69 | err_code); 70 | 71 | (void) err_code; 72 | 73 | // Prevent the C library from buffering as otherwise it interferes with 74 | // the UART FIFO 75 | setbuf(stdout, NULL); 76 | } 77 | 78 | 79 | // **************************************************************************** 80 | // Setup the RTC to provide a TICK 81 | static void RTC_init(void) 82 | { 83 | static const nrf_drv_rtc_t rtc = NRF_DRV_RTC_INSTANCE(0); 84 | 85 | // Initialize and start RTC0, using the defaults from nrf_drv_config.h 86 | nrf_drv_rtc_init(&rtc, NULL, rtc_callback); 87 | nrf_drv_rtc_tick_enable(&rtc, true); 88 | nrf_drv_rtc_enable(&rtc); 89 | } 90 | 91 | 92 | // **************************************************************************** 93 | int main(void) 94 | { 95 | CLOCKS_init(); 96 | RTC_init(); 97 | UART_init(); 98 | RF_init(); 99 | 100 | while (true) { 101 | RF_service(); 102 | 103 | __WFE(); 104 | } 105 | } -------------------------------------------------------------------------------- /configurator/nrf51-nrf-uart-bridge/firmware/nrf_drv_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Board definition for WaveShare BLE400 development board 4 | 5 | #define NRF_MAXIMUM_LATENCY_US 2000 6 | 7 | 8 | #define TIMER1_ENABLED 1 9 | #define TIMER1_CONFIG_FREQUENCY NRF_TIMER_FREQ_1MHz 10 | #define TIMER1_CONFIG_MODE TIMER_MODE_MODE_Timer 11 | #define TIMER1_CONFIG_BIT_WIDTH TIMER_BITMODE_BITMODE_16Bit 12 | #define TIMER1_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW 13 | #define TIMER1_INSTANCE_INDEX 0 14 | #define TIMER_COUNT 1 15 | 16 | 17 | #define RTC0_ENABLED 1 18 | #define RTC0_CONFIG_FREQUENCY 1000 19 | #define RTC0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW 20 | #define RTC0_CONFIG_RELIABLE false 21 | #define RTC0_INSTANCE_INDEX 0 22 | #define RTC_COUNT 1 23 | 24 | 25 | #define UART0_ENABLED 1 26 | #define UART0_CONFIG_HWFC NRF_UART_HWFC_DISABLED 27 | #define UART0_CONFIG_PARITY NRF_UART_PARITY_EXCLUDED 28 | #define UART0_CONFIG_BAUDRATE NRF_UART_BAUDRATE_115200 29 | #define UART0_CONFIG_PSEL_TXD 9 30 | #define UART0_CONFIG_PSEL_RXD 11 31 | #define UART0_CONFIG_PSEL_CTS 10 32 | #define UART0_CONFIG_PSEL_RTS 8 33 | #define UART0_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_HIGH 34 | #define UART0_CONFIG_RX_BUFFER_SIZE 512 35 | #define UART0_CONFIG_TX_BUFFER_SIZE 512 36 | 37 | 38 | #define RNG_ENABLED 1 39 | #define RNG_CONFIG_ERROR_CORRECTION true 40 | #define RNG_CONFIG_POOL_SIZE 8 41 | #define RNG_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW 42 | -------------------------------------------------------------------------------- /configurator/nrf51-nrf-uart-bridge/firmware/rf_protocol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | uint32_t RF_init(void); 4 | void RF_service(void); 5 | -------------------------------------------------------------------------------- /configurator/nrf51-nrf-uart-bridge/firmware/slip.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // SLIP protocol implementation according to RFC1055 7 | // https://tools.ietf.org/html/rfc1055 8 | 9 | // Special SLIP character codes 10 | #define END 0xc0 // Start/end of packet 11 | #define ESC 0xdb // Byte stuffing 12 | #define ESC_END 0xdc // ESC ESC_END means END data byte 13 | #define ESC_ESC 0xdd // ESC ESC_ESC means ESC data byte 14 | 15 | 16 | // **************************************************************************** 17 | void SLIP_init(slip_t *s) 18 | { 19 | s->state = SLIP_IDLE; 20 | s->message_size = 0; 21 | } 22 | 23 | 24 | // **************************************************************************** 25 | bool SLIP_decode(slip_t *s, uint8_t new_input) 26 | { 27 | // If we are getting called after we received already a complete message, 28 | // re-initialize for receiving a new message 29 | if (s->state == SLIP_MESSAGE_RECEIVED) { 30 | SLIP_init(s); 31 | } 32 | 33 | // If the SLIP message is too long wait until it finishes, then start 34 | // capturing the next message. This means long messages are simply ignored. 35 | if (s->state == SLIP_OVERFLOW) { 36 | if (new_input == END) { 37 | SLIP_init(s); 38 | } 39 | return false; 40 | } 41 | 42 | switch (new_input) { 43 | case END: 44 | // We return True only if we received a message 45 | if (s->message_size) { 46 | s->state = SLIP_MESSAGE_RECEIVED; 47 | return true; 48 | } 49 | return false; 50 | 51 | case ESC: 52 | s->state = SLIP_ESC; 53 | break; 54 | 55 | default: 56 | if (s->state == SLIP_ESC) { 57 | s->state = SLIP_IDLE; 58 | switch (new_input) { 59 | case ESC_ESC: 60 | new_input = ESC; 61 | break; 62 | 63 | case ESC_END: 64 | new_input = END; 65 | break; 66 | 67 | // Protocol violation; handle it gracefully by ignoring ESC 68 | default: 69 | break; 70 | } 71 | } 72 | 73 | if (s->message_size < s->buffer_size) { 74 | s->buffer[s->message_size] = new_input; 75 | ++s->message_size; 76 | } 77 | else { 78 | s->state = SLIP_OVERFLOW; 79 | } 80 | break; 81 | } 82 | 83 | return false; 84 | } 85 | 86 | 87 | // **************************************************************************** 88 | void SLIP_encode(const uint8_t *data, uint8_t length, int (* callback)(int)) 89 | { 90 | if (callback == NULL || length == 0) { 91 | return; 92 | } 93 | 94 | callback(END); 95 | 96 | while (length) { 97 | 98 | switch (*data) { 99 | case END: 100 | callback(ESC); 101 | callback(ESC_END); 102 | break; 103 | 104 | case ESC: 105 | callback(ESC); 106 | callback(ESC_ESC); 107 | break; 108 | 109 | default: 110 | callback(*data); 111 | } 112 | 113 | --length; 114 | ++data; 115 | } 116 | 117 | callback(END); 118 | } -------------------------------------------------------------------------------- /configurator/nrf51-nrf-uart-bridge/firmware/slip.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | typedef enum { 8 | SLIP_IDLE = 0, 9 | SLIP_ESC, 10 | SLIP_OVERFLOW, 11 | SLIP_MESSAGE_RECEIVED 12 | } slip_state_t; 13 | 14 | typedef struct { 15 | uint8_t *buffer; 16 | uint8_t buffer_size; 17 | uint8_t message_size; 18 | slip_state_t state; 19 | } slip_t; 20 | 21 | 22 | void SLIP_init(slip_t *s); 23 | bool SLIP_decode(slip_t *s, uint8_t new_input); 24 | void SLIP_encode(const uint8_t *data, uint8_t length, int (* callback)(int)); 25 | -------------------------------------------------------------------------------- /configurator/orangepizero/README.md: -------------------------------------------------------------------------------- 1 | This folder contains files related to the Orange Pi Zero-based configurator bridge that allows to configure the headless transmitter via a Wi-Fi connected computer or Smartphone. 2 | 3 | Refer to [INSTALL.md](INSTALL.md) of how to configure the Orange Pi Zero. 4 | 5 | The [etc/](etc/) and [var/](var/) folders contains sample configuations as described in [INSTALL.md](INSTALL.md). 6 | 7 | The [mechanics/](mechanics/) folder contains mechanical files to 3D-print a case for the Orange Pi Zero. 8 | 9 | The [openocd/](openocd/) folder contains script and configuration files to flash the nRF51822 and STM32F103 from the Orange Pi Zero. Refer tp [openocd/README.md](openocd/README.md) for details. 10 | 11 | -------------------------------------------------------------------------------- /configurator/orangepizero/configurator-pcb1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/orangepizero/configurator-pcb1.jpg -------------------------------------------------------------------------------- /configurator/orangepizero/etc/apt/apt.conf.d/90no-auto-upgrades: -------------------------------------------------------------------------------- 1 | APT::Periodic::Update-Package-Lists "0"; 2 | APT::Periodic::Unattended-Upgrade "0"; 3 | -------------------------------------------------------------------------------- /configurator/orangepizero/etc/dnsmasq.d/configurator: -------------------------------------------------------------------------------- 1 | interface=wlan0 # Use interface wlan0 2 | listen-address=192.168.4.1 # Explicitly specify the address to listen on 3 | bind-interfaces # Bind to the interface to make sure we aren't sending things elsewhere 4 | server=8.8.8.8 # Forward DNS requests to Google DNS 5 | domain-needed # Don't forward short names 6 | bogus-priv # Never forward addresses in the non-routed address spaces. 7 | dhcp-range=192.168.4.50,192.168.4.150,12h # Assign IP addresses between 192.168.4.50 and 192.168.4.150 with a 12 hour lease time -------------------------------------------------------------------------------- /configurator/orangepizero/etc/hostapd.configurator.conf: -------------------------------------------------------------------------------- 1 | ssid=LANE Boys RC 2 | wpa_passphrase=12345678 3 | channel=11 4 | interface=wlan0 5 | driver=nl80211 6 | utf8_ssid=1 7 | wpa=2 8 | wpa_key_mgmt=WPA-PSK 9 | wpa_pairwise=CCMP 10 | auth_algs=1 11 | hw_mode=g 12 | ieee80211n=1 13 | max_num_sta=5 14 | wmm_enabled=1 15 | macaddr_acl=0 16 | -------------------------------------------------------------------------------- /configurator/orangepizero/etc/network/interfaces.hostapd-configurator: -------------------------------------------------------------------------------- 1 | allow-hotplug wlan0 2 | iface wlan0 inet static 3 | address 192.168.4.1 4 | netmask 255.255.255.0 5 | -------------------------------------------------------------------------------- /configurator/orangepizero/etc/nginx/sites-available/configurator: -------------------------------------------------------------------------------- 1 | map $http_upgrade $connection_upgrade { 2 | default upgrade; 3 | '' close; 4 | } 5 | 6 | server { 7 | listen 80 default_server; 8 | server_name _; 9 | return 301 https://$host$request_uri; 10 | } 11 | 12 | server { 13 | listen 443 ssl; 14 | server_name configurator; 15 | ssl on; 16 | include snippets/snakeoil.conf; 17 | 18 | root /var/www/html; 19 | index index.configurator.html index.html index.htm index.nginx-debian.html; 20 | location / { 21 | try_files $uri $uri/ =404; 22 | } 23 | 24 | location /ws { 25 | proxy_pass http://localhost:9706; 26 | proxy_http_version 1.1; 27 | proxy_set_header Upgrade $http_upgrade; 28 | proxy_set_header Connection $connection_upgrade; 29 | } 30 | } 31 | 32 | server { 33 | listen 9707 ssl; 34 | ssl on; 35 | include snippets/snakeoil.conf; 36 | 37 | location / { 38 | proxy_pass http://localhost:9706; 39 | proxy_http_version 1.1; 40 | proxy_set_header Upgrade $http_upgrade; 41 | proxy_set_header Connection $connection_upgrade; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /configurator/orangepizero/etc/overlayroot.local.conf: -------------------------------------------------------------------------------- 1 | overlayroot="tmpfs" 2 | 3 | -------------------------------------------------------------------------------- /configurator/orangepizero/etc/udev/rules.d/99-gpio.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="gpio", ACTION=="add", PROGRAM="/bin/sh -c 'chown -R root:gpio /sys/class/gpio && chmod -R g+w /sys/class/gpio'" 2 | SUBSYSTEM=="gpio", ACTION=="add", PROGRAM="/bin/sh -c 'chown -R root:gpio /sys/devices/virtual/gpio && chmod -R g+w /sys/devices/virtual/gpio'" 3 | SUBSYSTEM=="gpio", ACTION=="add", PROGRAM="/bin/sh -c 'chown -R root:gpio /sys/devices/platform/sunxi-pinctrl/gpio && chmod -R g+w /sys/devices/platform/sunxi-pinctrl/gpio'" 4 | -------------------------------------------------------------------------------- /configurator/orangepizero/install-dependencies: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sudo apt-get install dnsmasq iptables iptables-persistent nginx-light openssl ssl-cert 4 | 5 | echo "Note: you still need to install nodejs manually; follow the documentation!" 6 | -------------------------------------------------------------------------------- /configurator/orangepizero/mechanics/configurator-label.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/orangepizero/mechanics/configurator-label.pdf -------------------------------------------------------------------------------- /configurator/orangepizero/openocd/README.md: -------------------------------------------------------------------------------- 1 | If you don't have a SWD programmer to flash the nRF51822 or the STM32F103, you can use the Orange Pi Zero in conjuction with the Open OCD. 2 | 3 | 4 | git clone https://github.com/ntfreak/openocd 5 | sudo apt-get install libtool texinfo pkg-config 6 | 7 | cd openocd 8 | ./bootstrap 9 | ./configure --enable-sysfsgpio 10 | make 11 | sudo make install 12 | 13 | This folder contains two configuration scripts and shell scripts to flash the nRF51822 or STM32F103C8T6. They use different GPIO ports on the Orange Pi Zero, which means the nRF51822 can stay connected when flashing the STM32F103. 14 | 15 | The nRF51822 uses pins 16 | 17 | 3 GPIO12 SWCLK 18 | 5 GPIO11 SWDIO 19 | 20 | the STM32F103 uses pins 21 | 22 | 16 GPIO19 SWCLK 23 | 18 GPIO18 SWDIO 24 | -------------------------------------------------------------------------------- /configurator/orangepizero/openocd/flash-nrf51: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Important: we manually export the used GPIO pins as doing so within openocd 4 | # fails. The failure is caused by openocd accessing the GPIO too quickly 5 | # after exporting, which does not give udev enough time to modify the 6 | # permissions. 7 | # We could run openocd as root, but that is not desireable 8 | echo 11 > /sys/class/gpio/export 9 | echo 12 > /sys/class/gpio/export 10 | sleep 1 11 | 12 | openocd -f orangepizero-nrf51.cfg -f target/nrf51.cfg -c "program ../../../bin/nrf51-uart-bridge.hex reset exit" 13 | -------------------------------------------------------------------------------- /configurator/orangepizero/openocd/flash-stm32f103c8t6: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Important: we manually export the used GPIO pins as doing so within openocd 4 | # fails. The failure is caused by openocd accessing the GPIO too quickly 5 | # after exporting, which does not give udev enough time to modify the 6 | # permissions. 7 | # We could run openocd as root, but that is not desireable 8 | echo 18 > /sys/class/gpio/export 9 | echo 19 > /sys/class/gpio/export 10 | sleep 1 11 | 12 | openocd -f orangepizero-stm32f1.cfg -f target/stm32f1x.cfg -c "program ../../../bin/tx.hex reset exit" 13 | -------------------------------------------------------------------------------- /configurator/orangepizero/openocd/orangepizero-nrf51.cfg: -------------------------------------------------------------------------------- 1 | interface sysfsgpio 2 | 3 | # GPIO numbers for SWCLK SWDIO 4 | sysfsgpio_swd_nums 12 11 5 | 6 | transport select swd 7 | -------------------------------------------------------------------------------- /configurator/orangepizero/openocd/orangepizero-stm32f1.cfg: -------------------------------------------------------------------------------- 1 | interface sysfsgpio 2 | 3 | # GPIO numbers for SWCLK SWDIO 4 | sysfsgpio_swd_nums 19 18 5 | 6 | transport select swd 7 | -------------------------------------------------------------------------------- /configurator/orangepizero/shutdown-on-button-press.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | 3 | import os 4 | import sys 5 | import time 6 | 7 | gpioNumber = "6" 8 | export = '/sys/class/gpio/export' 9 | direction = '/sys/class/gpio/gpio' + gpioNumber + '/direction' 10 | value = '/sys/class/gpio/gpio' + gpioNumber + '/value' 11 | timeout = time.time() 12 | 13 | def shutdown(): 14 | print('Shutting down ...') 15 | os.system('/bin/sh -c "sudo poweroff"') 16 | sys.exit(0) 17 | 18 | try: 19 | with open(export, 'w') as f: 20 | f.write(gpioNumber) 21 | except IOError: 22 | pass 23 | 24 | time.sleep(0.1) 25 | 26 | with open(direction, 'w') as f: 27 | f.write('in') 28 | 29 | with open(value, 'r') as f: 30 | print('Monitoring GPIO' + gpioNumber) 31 | while True: 32 | f.seek(0) 33 | newValue = int(f.read()) 34 | 35 | if newValue: 36 | timeout = time.time() 37 | 38 | 39 | if (time.time() - timeout) > 2.0: 40 | shutdown() 41 | 42 | time.sleep(0.1) 43 | 44 | -------------------------------------------------------------------------------- /configurator/usb-dongle/electronics/.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.kicad_pcb-bak 3 | -------------------------------------------------------------------------------- /configurator/usb-dongle/electronics/rescue-backup/usb-dongle-2018-08-21-16-41-39.pro: -------------------------------------------------------------------------------- 1 | update=Mon 05 Mar 2018 11:17:45 AM +08 2 | version=1 3 | last_client=kicad 4 | [pcbnew] 5 | version=1 6 | LastNetListRead= 7 | UseCmpFile=1 8 | PadDrill=0.600000000000 9 | PadDrillOvalY=0.600000000000 10 | PadSizeH=1.500000000000 11 | PadSizeV=1.500000000000 12 | PcbTextSizeV=1.500000000000 13 | PcbTextSizeH=1.500000000000 14 | PcbTextThickness=0.300000000000 15 | ModuleTextSizeV=1.000000000000 16 | ModuleTextSizeH=1.000000000000 17 | ModuleTextSizeThickness=0.150000000000 18 | SolderMaskClearance=0.000000000000 19 | SolderMaskMinWidth=0.000000000000 20 | DrawSegmentWidth=0.200000000000 21 | BoardOutlineThickness=0.100000000000 22 | ModuleOutlineThickness=0.150000000000 23 | [pcbnew/libraries] 24 | LibName1=WLA-power 25 | LibName2=WLA-nrf24 26 | LibName3=sockets 27 | LibName4=connect 28 | LibName5=discret 29 | LibName6=pin_array 30 | LibName7=divers 31 | LibName8=smd_capacitors 32 | LibName9=smd_resistors 33 | LibName10=smd_crystal&oscillator 34 | LibName11=smd_dil 35 | LibName12=smd_transistors 36 | LibName13=libcms 37 | LibName14=display 38 | LibName15=led 39 | LibName16=dip_sockets 40 | LibName17=pga_sockets 41 | LibName18=valves 42 | LibDir=/home/laneboysrc/projects/kicad-libraries 43 | [general] 44 | version=1 45 | [cvpcb] 46 | version=1 47 | NetIExt=net 48 | [cvpcb/libraries] 49 | EquName1=devcms 50 | [eeschema] 51 | version=1 52 | LibDir=/home/laneboysrc/projects/kicad-libraries 53 | [eeschema/libraries] 54 | LibName1=usb-dongle-rescue 55 | LibName2=WLA-STM32 56 | LibName3=WLA-nrf24 57 | LibName4=WLA-power 58 | LibName5=power 59 | LibName6=device 60 | LibName7=conn 61 | LibName8=regul 62 | LibName9=opto 63 | LibName10=contrib 64 | LibName11=analog_switches 65 | LibName12=display 66 | LibName13=interface 67 | LibName14=references 68 | LibName15=rfcom 69 | -------------------------------------------------------------------------------- /configurator/usb-dongle/electronics/rescue-backup/usb-dongle-rescue-2018-08-21-16-41-39.lib: -------------------------------------------------------------------------------- 1 | EESchema-LIBRARY Version 2.3 2 | #encoding utf-8 3 | # 4 | #End Library 5 | -------------------------------------------------------------------------------- /configurator/usb-dongle/electronics/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (lib (name usb-dongle-rescue)(type Legacy)(uri ${KIPRJMOD}/usb-dongle-rescue.lib)(options "")(descr "")) 3 | (lib (name WLA-STM32)(type Legacy)(uri /home/laneboysrc/projects/kicad-libraries/WLA-STM32.lib)(options "")(descr "")) 4 | (lib (name WLA-nrf24)(type Legacy)(uri /home/laneboysrc/projects/kicad-libraries/WLA-nrf24.lib)(options "")(descr "")) 5 | (lib (name WLA-power)(type Legacy)(uri /home/laneboysrc/projects/kicad-libraries/WLA-power.lib)(options "")(descr "")) 6 | (lib (name WLA-nrf51822-module)(type Legacy)(uri /home/laneboysrc/projects/kicad-libraries/WLA-nrf51822-module.lib)(options "")(descr "")) 7 | ) 8 | -------------------------------------------------------------------------------- /configurator/usb-dongle/electronics/usb-dongle-rescue.lib: -------------------------------------------------------------------------------- 1 | EESchema-LIBRARY Version 2.4 2 | #encoding utf-8 3 | # 4 | # C 5 | # 6 | DEF C C 0 10 N Y 1 F N 7 | F0 "C" 25 100 50 H V L CNN 8 | F1 "C" 25 -100 50 H V L CNN 9 | F2 "" 38 -150 50 H I C CNN 10 | F3 "" 0 0 50 H I C CNN 11 | $FPLIST 12 | C_* 13 | $ENDFPLIST 14 | DRAW 15 | P 2 0 1 20 -80 -30 80 -30 N 16 | P 2 0 1 20 -80 30 80 30 N 17 | X ~ 1 0 150 110 D 50 50 1 1 P 18 | X ~ 2 0 -150 110 U 50 50 1 1 P 19 | ENDDRAW 20 | ENDDEF 21 | # 22 | #End Library 23 | -------------------------------------------------------------------------------- /configurator/usb-dongle/electronics/usb-dongle.pro: -------------------------------------------------------------------------------- 1 | update=Tue 21 Aug 2018 04:41:45 PM +08 2 | version=1 3 | last_client=kicad 4 | [pcbnew] 5 | version=1 6 | LastNetListRead= 7 | UseCmpFile=1 8 | PadDrill=0.600000000000 9 | PadDrillOvalY=0.600000000000 10 | PadSizeH=1.500000000000 11 | PadSizeV=1.500000000000 12 | PcbTextSizeV=1.500000000000 13 | PcbTextSizeH=1.500000000000 14 | PcbTextThickness=0.300000000000 15 | ModuleTextSizeV=1.000000000000 16 | ModuleTextSizeH=1.000000000000 17 | ModuleTextSizeThickness=0.150000000000 18 | SolderMaskClearance=0.000000000000 19 | SolderMaskMinWidth=0.000000000000 20 | DrawSegmentWidth=0.200000000000 21 | BoardOutlineThickness=0.100000000000 22 | ModuleOutlineThickness=0.150000000000 23 | [pcbnew/libraries] 24 | LibName1=WLA-power 25 | LibName2=WLA-nrf24 26 | LibName3=sockets 27 | LibName4=connect 28 | LibName5=discret 29 | LibName6=pin_array 30 | LibName7=divers 31 | LibName8=smd_capacitors 32 | LibName9=smd_resistors 33 | LibName10=smd_crystal&oscillator 34 | LibName11=smd_dil 35 | LibName12=smd_transistors 36 | LibName13=libcms 37 | LibName14=display 38 | LibName15=led 39 | LibName16=dip_sockets 40 | LibName17=pga_sockets 41 | LibName18=valves 42 | LibDir=/home/laneboysrc/projects/kicad-libraries 43 | [general] 44 | version=1 45 | [cvpcb] 46 | version=1 47 | NetIExt=net 48 | [cvpcb/libraries] 49 | EquName1=devcms 50 | [eeschema] 51 | version=1 52 | LibDir= 53 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/.gitignore: -------------------------------------------------------------------------------- 1 | _build/* 2 | openocd.log 3 | st-link-serial.cfg 4 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the firmware that implements a WebUSB compatible dongle. The dongle can be used by the web-app when using a browser with WebUSB support (currently Chrome/Chromium only). 2 | 3 | It runs on the STM32F103C8T6 micro-controller that controls the nrf51-nrf-uart-bridge. 4 | 5 | The software makes used of [libopencm3](http://libopencm3.org/). 6 | 7 | The firmware is built using a [GNU Make](https://www.gnu.org/software/make/) makefile. The compiler is [ARM GCC toolchain](https://launchpad.net/gcc-arm-embedded), version 5.2.1. [OpenOCD](http://openocd.org/) is used to program the firmware into the micro-controller. 8 | 9 | In the [Makefile](Makefile), adjust the variable `GNU_INSTALL_ROOT` and `OPENOCD` to match your installation. 10 | 11 | Run 12 | 13 | make 14 | 15 | to build the firmware. When done, run 16 | 17 | make program 18 | 19 | to flash the firmware into the micro-controller. 20 | 21 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/gdb.flash.script: -------------------------------------------------------------------------------- 1 | target extended-remote /dev/ttyACM0 2 | monitor swdp_scan 3 | attach 1 4 | file _build/usb-dongle.out 5 | load 6 | kill 7 | quit 8 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | // #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | // **************************************************************************** 18 | static void clock_init(void) 19 | { 20 | rcc_clock_setup_in_hsi_out_48mhz(); 21 | 22 | // Enable clocks for GPIO ports 23 | // IMPORTANT: you can not 'or' them into one call due to bit-mangling 24 | rcc_periph_clock_enable(RCC_GPIOA); 25 | rcc_periph_clock_enable(RCC_GPIOB); 26 | rcc_periph_clock_enable(RCC_GPIOC); 27 | rcc_periph_clock_enable(RCC_AFIO); 28 | } 29 | 30 | 31 | // **************************************************************************** 32 | int main(void) 33 | { 34 | clock_init(); 35 | SYSTICK_init(); 36 | UART_init(); 37 | WEBUSB_init(); 38 | WATCHDOG_start(); 39 | LED_init(); 40 | printf("\n\n\n**********\nUSB dongle initialized\n"); 41 | 42 | while (1) { 43 | WATCHDOG_reset(); 44 | WEBUSB_poll(); 45 | } 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/stm32f103c8t6.ld: -------------------------------------------------------------------------------- 1 | /* Linker script for AliExpress STM32F103 board (STM32F103C8T6, 64K flash, 20K RAM). */ 2 | 3 | MEMORY 4 | { 5 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 64K 6 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 7 | } 8 | 9 | 10 | /* Put the transmitter configuration in the upper-most 8 KBytes of flash */ 11 | SECTIONS 12 | { 13 | .persistent_settings 0x0800e000 : 14 | { 15 | *(.rodata.logical_inputs_flash) 16 | } 17 | .transmitter_configuration 0x0800e400 : 18 | { 19 | *(.rodata.config_flash) 20 | } 21 | } 22 | 23 | 24 | INCLUDE libopencm3_stm32f1.ld 25 | 26 | 27 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/system/led.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | static uint8_t led_active; 9 | 10 | 11 | // **************************************************************************** 12 | // LED activity indicator 13 | // 14 | // Every time an activity occurs the LED flashes for a brief moment. In idle 15 | // state the LED is on to indicate that the device is powered. 16 | // 17 | // The LED flashes one period before re-triggering is allowed. 18 | // 19 | // Note that the LED is active low. 20 | static void led_pulse_done(void) 21 | { 22 | if (gpio_get(GPIOC, GPIO13)) { 23 | gpio_clear(GPIOC, GPIO13); 24 | SYSTICK_set_callback(led_pulse_done, 40); 25 | } 26 | else { 27 | led_active = 0; 28 | } 29 | } 30 | 31 | 32 | // **************************************************************************** 33 | void LED_pulse(void) 34 | { 35 | if (led_active) { 36 | return; 37 | } 38 | 39 | led_active = 1; 40 | gpio_set(GPIOC, GPIO13); 41 | SYSTICK_set_callback(led_pulse_done, 40); 42 | } 43 | 44 | 45 | // **************************************************************************** 46 | void LED_init(void) 47 | { 48 | // LED off (until the first LED_pulse call) 49 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); 50 | gpio_set(GPIOC, GPIO13); 51 | } -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/system/led.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void LED_init(void); 4 | void LED_pulse(void); 5 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/system/serial_number.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | // Build a serial number string from the chip's unique ID. 6 | // The input buffer must have a size of at least 9 char. 7 | void SERIAL_NUMBER_get(char *buffer) 8 | { 9 | volatile uint32_t *unique_id_p = (volatile uint32_t *)0x1FFFF7E8; 10 | 11 | uint8_t i; 12 | uint32_t unique_id; 13 | 14 | // Combine all unique ID fields into a single 32-bit number 15 | unique_id = *unique_id_p + *(unique_id_p + 1) + *(unique_id_p + 2); 16 | 17 | // Split the unique ID into 8 nibbles 18 | for (i = 0; i < 8; i++) { 19 | buffer[i] = unique_id & 0x0f; 20 | unique_id = unique_id >> 4; 21 | } 22 | 23 | // Convert each nibble into a HEX digit 24 | for (i = 0; i < 8; i++) { 25 | if (buffer[i] > 9) { 26 | buffer[i] += 'A' - 10; 27 | } 28 | else { 29 | buffer[i] += '0'; 30 | } 31 | } 32 | 33 | // Terminate the buffer to form a C-string 34 | buffer[8] = 0; 35 | } 36 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/system/serial_number.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void SERIAL_NUMBER_get(char *serial_number_buffer); 4 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/system/systick.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | 12 | // **************************************************************************** 13 | #define MAX_SYSTICK_CALLBACKS 5 14 | 15 | 16 | typedef struct { 17 | systick_callback callback; 18 | uint32_t trigger_ms; 19 | } systick_callback_t; 20 | 21 | 22 | volatile uint32_t milliseconds; 23 | 24 | static systick_callback_t callbacks[MAX_SYSTICK_CALLBACKS]; 25 | 26 | 27 | // **************************************************************************** 28 | static systick_callback_t *find_callback(systick_callback cb) 29 | { 30 | for (size_t i = 0; i < MAX_SYSTICK_CALLBACKS; i++) { 31 | if (callbacks[i].callback == cb) { 32 | return &callbacks[i]; 33 | } 34 | } 35 | 36 | return NULL; 37 | } 38 | 39 | 40 | // **************************************************************************** 41 | static systick_callback_t *get_emtpy_callback_slot(void) 42 | { 43 | return find_callback(NULL); 44 | } 45 | 46 | 47 | // **************************************************************************** 48 | void SYSTICK_init(void) 49 | { 50 | uint32_t reload_value; 51 | 52 | // 24 MHz / 8 => 3000000 counts per second 53 | systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); 54 | 55 | // SysTick interrupt every 1 ms 56 | reload_value = (rcc_ahb_frequency / 8 / 1000) - 1; 57 | systick_set_reload(reload_value); 58 | 59 | nvic_set_priority(NVIC_SYSTICK_IRQ, 0x0); 60 | systick_interrupt_enable(); 61 | systick_counter_enable(); 62 | } 63 | 64 | 65 | // **************************************************************************** 66 | void SYSTICK_set_callback(systick_callback cb, uint32_t duration_ms) 67 | { 68 | systick_callback_t *slot; 69 | 70 | if (duration_ms == 0) { 71 | duration_ms = 1; 72 | } 73 | 74 | slot = find_callback(cb); 75 | if (slot == NULL) { 76 | slot = get_emtpy_callback_slot(); 77 | if (slot == NULL) { 78 | printf("ALERT: MAX_SYSTICK_CALLBACKS needs to be increased!\n"); 79 | return; 80 | } 81 | } 82 | 83 | cm_disable_interrupts(); 84 | slot->callback = cb; 85 | slot->trigger_ms = milliseconds + duration_ms; 86 | cm_enable_interrupts(); 87 | } 88 | 89 | 90 | // **************************************************************************** 91 | void SYSTICK_clear_callback(systick_callback cb) 92 | { 93 | systick_callback_t *slot; 94 | 95 | slot = find_callback(cb); 96 | if (slot) { 97 | cm_disable_interrupts(); 98 | slot->callback = NULL; 99 | cm_enable_interrupts(); 100 | } 101 | } 102 | 103 | 104 | // **************************************************************************** 105 | void sys_tick_handler(void) 106 | { 107 | ++milliseconds; 108 | 109 | for (size_t i = 0; i < MAX_SYSTICK_CALLBACKS; i++) { 110 | if (callbacks[i].callback != NULL && 111 | callbacks[i].trigger_ms == milliseconds ) { 112 | 113 | systick_callback cb = callbacks[i].callback; 114 | callbacks[i].callback = NULL; 115 | (*cb)(); 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/system/systick.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | typedef void (* systick_callback)(void); 7 | 8 | extern volatile uint32_t milliseconds; 9 | 10 | void SYSTICK_init(void); 11 | void SYSTICK_set_callback(systick_callback cb, uint32_t duration_ms); 12 | void SYSTICK_clear_callback(systick_callback cb); 13 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/system/uart.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void UART_init(void); 4 | void UART_sync(void); 5 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/system/watchdog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | 7 | // **************************************************************************** 8 | void WATCHDOG_start(void) 9 | { 10 | iwdg_set_period_ms(100); 11 | iwdg_start(); 12 | } 13 | 14 | 15 | // **************************************************************************** 16 | void WATCHDOG_reset(void) 17 | { 18 | iwdg_reset(); 19 | } 20 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/system/watchdog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void WATCHDOG_start(void); 4 | void WATCHDOG_reset(void); 5 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/usb/webusb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void WEBUSB_poll(void); 4 | void WEBUSB_init(void); 5 | void WEBUSB_putc(char c); 6 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/utils/ring_buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | // Ring buffer implementation from open-bldc's libgovernor 8 | // https://github.com/open-bldc/open-bldc/tree/master/source/libgovernor 9 | // 10 | // Heavily modified: all functions return the number of bytes read/written 11 | 12 | 13 | // **************************************************************************** 14 | void RING_BUFFER_init(RING_BUFFER_T *ring, uint8_t *buf, RING_BUFFER_SIZE_T size) 15 | { 16 | ring->data = buf; 17 | ring->size = size; 18 | ring->begin = 0; 19 | ring->end = 0; 20 | } 21 | 22 | 23 | // **************************************************************************** 24 | RING_BUFFER_SIZE_T RING_BUFFER_write_uint8(RING_BUFFER_T *ring, uint8_t value) 25 | { 26 | if (((ring->end + 1) % ring->size) != ring->begin) { 27 | ring->data[ring->end] = value; 28 | ring->end = (ring->end + 1) % ring->size; 29 | return 1; 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | 36 | // **************************************************************************** 37 | RING_BUFFER_SIZE_T RING_BUFFER_write(RING_BUFFER_T *ring, uint8_t *data, RING_BUFFER_SIZE_T size) 38 | { 39 | RING_BUFFER_SIZE_T i; 40 | 41 | for (i = 0; i < size; i++) { 42 | if (RING_BUFFER_write_uint8(ring, *data) == 0) { 43 | break; 44 | } 45 | ++data; 46 | } 47 | 48 | return i; 49 | } 50 | 51 | 52 | // **************************************************************************** 53 | RING_BUFFER_SIZE_T RING_BUFFER_read_uint8(RING_BUFFER_T *ring, uint8_t *data) 54 | { 55 | if (data != NULL) { 56 | if (ring->begin != ring->end) { 57 | *data = ring->data[ring->begin]; 58 | ring->begin = (ring->begin + 1) % ring->size; 59 | return 1; 60 | } 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | 67 | // **************************************************************************** 68 | RING_BUFFER_SIZE_T RING_BUFFER_read(RING_BUFFER_T *ring, uint8_t *data, RING_BUFFER_SIZE_T size) 69 | { 70 | RING_BUFFER_SIZE_T i; 71 | 72 | for (i = 0; i < size; i++) { 73 | if (RING_BUFFER_read_uint8(ring, data) == 0) { 74 | break; 75 | } 76 | ++data; 77 | } 78 | 79 | return i; 80 | } 81 | 82 | 83 | // **************************************************************************** 84 | bool RING_BUFFER_is_empty(RING_BUFFER_T *ring) 85 | { 86 | return (ring->begin == ring->end); 87 | } 88 | -------------------------------------------------------------------------------- /configurator/usb-dongle/firmware/utils/ring_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef uint16_t RING_BUFFER_SIZE_T; 7 | 8 | typedef struct { 9 | uint8_t *data; 10 | RING_BUFFER_SIZE_T size; 11 | uint32_t begin; 12 | uint32_t end; 13 | } RING_BUFFER_T; 14 | 15 | void RING_BUFFER_init(RING_BUFFER_T *ring, uint8_t *buf, RING_BUFFER_SIZE_T size); 16 | RING_BUFFER_SIZE_T RING_BUFFER_write(RING_BUFFER_T *ring, uint8_t *data, RING_BUFFER_SIZE_T size); 17 | RING_BUFFER_SIZE_T RING_BUFFER_write_uint8(RING_BUFFER_T *ring, uint8_t data); 18 | RING_BUFFER_SIZE_T RING_BUFFER_read(RING_BUFFER_T *ring, uint8_t *data, RING_BUFFER_SIZE_T size); 19 | RING_BUFFER_SIZE_T RING_BUFFER_read_uint8(RING_BUFFER_T *ring, uint8_t *data); 20 | bool RING_BUFFER_is_empty(RING_BUFFER_T *ring); 21 | -------------------------------------------------------------------------------- /configurator/usb-dongle/webusb/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WebUSB test 5 | 6 | 7 | 8 |
9 | 10 | 11 |
12 |
13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /configurator/web-app/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /configurator/web-app/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Werner Lane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is furnished 10 | to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /configurator/web-app/app/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "sourceType": "module", 10 | "ecmaVersion": 2017 11 | }, 12 | "rules": { 13 | "indent": [ 14 | "error", 15 | 2 16 | ], 17 | "linebreak-style": [ 18 | "error", 19 | "unix" 20 | ], 21 | "quotes": [ 22 | "error", 23 | "single" 24 | ], 25 | "semi": [ 26 | "error", 27 | "always" 28 | ], 29 | "no-console": ["off"] 30 | }, 31 | "globals": { 32 | "Device": true, 33 | "Database": true, 34 | "DatabaseObject": true, 35 | "MDLHelper": true, 36 | "Utils": true, 37 | "WebsocketTransport": true, 38 | "WebusbTransport": true, 39 | 40 | "About": true, 41 | "DeviceList": true, 42 | "EditCurve": true, 43 | "EditSwitch": true, 44 | "HardwareInputs": true, 45 | "Limits": true, 46 | "LogicalInputs": true, 47 | "Main": true, 48 | "Mixer": true, 49 | "MixerUnit": true, 50 | "ModelDetails": true, 51 | "ModelList": true, 52 | "Path": true, 53 | "RFProtocol": true, 54 | "SelectIcon": true, 55 | "SelectMultiple": true, 56 | "SelectSingle": true, 57 | "TransmitterDetails": true, 58 | "TransmitterList": true, 59 | 60 | "componentHandler": true, 61 | "strftime": true, 62 | "Sortable": true, 63 | 64 | "TEST_CONFIG_DATA": true, 65 | "CONFIG_VERSIONS": true, 66 | "VERSION_HASH": true, 67 | "VERSION_DATE": true, 68 | "VERSION_DIRTY": true 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /configurator/web-app/app/fonts/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/fonts/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /configurator/web-app/app/fonts/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/fonts/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /configurator/web-app/app/fonts/MaterialIcons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/fonts/MaterialIcons-Regular.woff -------------------------------------------------------------------------------- /configurator/web-app/app/fonts/MaterialIcons-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/fonts/MaterialIcons-Regular.woff2 -------------------------------------------------------------------------------- /configurator/web-app/app/fonts/materialicons.css: -------------------------------------------------------------------------------- 1 | /* fallback */ 2 | @font-face { 3 | font-family: 'Material Icons'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url(MaterialIcons-Regular.eot); /* For IE6-8 */ 7 | src: local('Material Icons'), 8 | local('MaterialIcons-Regular'), 9 | url(MaterialIcons-Regular.woff2) format('woff2'), 10 | url(MaterialIcons-Regular.woff) format('woff'), 11 | url(MaterialIcons-Regular.ttf) format('truetype'); 12 | } 13 | 14 | .material-icons { 15 | font-family: 'Material Icons'; 16 | font-weight: normal; 17 | font-style: normal; 18 | font-size: 24px; /* Preferred icon size */ 19 | display: inline-block; 20 | line-height: 1; 21 | text-transform: none; 22 | letter-spacing: normal; 23 | word-wrap: normal; 24 | white-space: nowrap; 25 | direction: ltr; 26 | 27 | /* Support for all WebKit browsers. */ 28 | -webkit-font-smoothing: antialiased; 29 | /* Support for Safari and Chrome. */ 30 | text-rendering: optimizeLegibility; 31 | 32 | /* Support for Firefox. */ 33 | -moz-osx-font-smoothing: grayscale; 34 | 35 | /* Support for IE. */ 36 | font-feature-settings: 'liga'; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {% block title %}Headless RC Transmitter configuration tool{% endblock %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {% block head %}{% endblock %} 36 | 37 | 38 | {% block content %} 39 | {% for p in ["main", "about", "device_list", "edit_curve", "edit_switch", "limits", "mixer", "mixer_unit", "model_details", "model_list", "rf_protocol", "select_multiple", "select_icon", "select_single", "settings", "transmitter_details", "transmitter_list", "hardware_inputs", "logical_inputs", "hardware_inputs_order"] %} 40 | 44 | {% endfor %} 45 | {% endblock %} 46 | 47 | {% include "app/html/pages/dialogs.html" %} 48 | 49 | {% block javascript %} 50 | {% endblock %} 51 | 52 | 56 | 57 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Headless RC Transmitter configuration tool 8 | 9 | 53 | 54 | 55 | 56 |
57 |
58 |
59 |
60 | 61 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/about.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 |
8 | About 9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 |
17 |
18 |
19 | 20 |

LANE Boys RC

21 |
Us two boys having fun with RC models!
22 |
23 | Version: 24 |
25 | Date: 26 |
27 |
28 | 33 |
34 |
35 | 36 | 37 |
38 |
39 |
40 | 41 |
42 | 47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 | 55 |
56 | 61 |
62 |
63 | 64 | 65 |
66 |
67 |
68 |

Thanks!

69 |
70 |
71 | This project would not have been possible without the LANE Boys being 72 | able to stand on the shoulders of giants. 73 |
74 |
75 | Credits 76 |
77 |
78 |
79 | 80 |
81 |
82 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/dialogs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Hop channels

5 |
6 |

7 | If you are using an 8 | original HobbyKing receiver, choose HobbyKing compatible only. 9 |

10 | 14 | 18 | 22 |
23 |
24 | 25 | 26 |
27 |
28 | 29 | 30 |

Adjust value

31 |
32 | 35 |
36 | 37 |
38 | 41 |
42 |
43 | 44 |
45 |
46 | 47 |
48 |
49 | 50 |
51 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/edit_switch.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 |
8 | Switch 9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 |
17 |
18 |
19 |

Switch function

20 |
21 |
22 | Configure what state a switch needs to be in to enable the 23 | corresponding mixer unit. 24 |
25 |
26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 53 | 54 | 55 | 56 | 59 | 62 | 63 | 64 |
31 | Switch: 32 | 36 | 39 |
44 | Comparison: 45 | 49 | 52 |
57 | Value: 58 | 60 | 61 |
65 | 66 |
67 |
68 |
69 | 70 |
71 |
72 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/hardware_inputs.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 |
8 | Hardware inputs 9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 | 44 | 45 |
46 |
47 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/hardware_inputs_order.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 |
8 | Hardware inputs 9 |
10 |
11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 |

Hardware input order

19 |
20 |
21 | Select the hardware inputs that belong to this logical input. Change 22 | the order of the selected hardware inputs below. 23 |
24 |
25 | 28 |
29 |
30 |
31 | 32 | 33 | 52 | 53 |
54 |
55 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Headless RC Transmitter 5 |
6 |
7 | 8 |
9 | 10 | 11 | 12 | 18 |
19 | 20 |
21 |
22 |
23 |
24 | 25 | Welcome! 26 |
27 |
28 | This is a configuration tool for the Headless RC Transmitter, an open source 2.4 GHz transmitter for radio controlled models. 29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 |

Configure a transmitter

37 |
38 |
39 | Find available transmitters and select one to configure. 40 |
41 |
42 | Wi-Fi 43 | 47 |
48 |
49 | 52 |
53 |
54 |
55 |
56 |
57 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/select_icon.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 |
8 | Pick an icon 9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 |
22 |

Item name

23 |
24 | 25 |
26 | 27 | 35 | 36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 | 46 |
47 |
48 | 49 |
50 |
51 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/select_multiple.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 |
8 | Pick up to items 9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 |
17 |
18 |
19 |

20 |
21 |
Item description.
22 | 23 |
24 | 25 | 31 | 32 |
33 |
34 |
35 | 36 | 37 |
38 |
39 | 42 |
43 |
44 | 45 |
46 |
47 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/select_single.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 |
8 | Pick an item 9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 |
22 |

Item name

23 |
24 |
25 | Item description. 26 |
27 | 28 |
29 | 30 | 36 | 37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 | 47 |
48 |
49 | 50 |
51 |
52 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/settings.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 |
8 | Settings 9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 |
17 |
18 |
19 |

Backup

20 |
21 |
22 | Backup your transmitter and model datbase regularly. 23 |
24 | When performing a backup, the data is stored in the download folder of your web 25 | browser. To restore data, select a previoulsy saved backup file. 26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 |
34 |
35 | 36 |
37 |
38 | -------------------------------------------------------------------------------- /configurator/web-app/app/html/pages/transmitter_list.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 7 |
8 | Transmitters 9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 | 26 | 27 | 28 |
29 |
30 |
31 |

Transmitter

32 |
33 |
34 | Select a transmitter to view or modify. 35 |
36 |
37 | 38 | 49 | 50 |
51 |
52 |
53 | 54 | 55 |
56 |
57 |
58 |

No transmitter

59 |
60 |
61 | Currently there is no transmitter stored in the database. Please connect a transmitter to load its setting. 62 |
63 |
64 |
65 | 66 | 67 | 68 | 69 |
70 |
71 | -------------------------------------------------------------------------------- /configurator/web-app/app/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/favicon-16x16.png -------------------------------------------------------------------------------- /configurator/web-app/app/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/favicon-32x32.png -------------------------------------------------------------------------------- /configurator/web-app/app/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/favicon.ico -------------------------------------------------------------------------------- /configurator/web-app/app/images/laneboysrc-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/laneboysrc-banner.png -------------------------------------------------------------------------------- /configurator/web-app/app/images/laneboysrc-blog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/laneboysrc-blog.jpg -------------------------------------------------------------------------------- /configurator/web-app/app/images/laneboysrc-logo-128-t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/laneboysrc-logo-128-t.png -------------------------------------------------------------------------------- /configurator/web-app/app/images/laneboysrc-logo-128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/laneboysrc-logo-128.jpg -------------------------------------------------------------------------------- /configurator/web-app/app/images/laneboysrc-logo-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/laneboysrc-logo-144.png -------------------------------------------------------------------------------- /configurator/web-app/app/images/laneboysrc-logo-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/laneboysrc-logo-180.png -------------------------------------------------------------------------------- /configurator/web-app/app/images/laneboysrc-logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/laneboysrc-logo-192.png -------------------------------------------------------------------------------- /configurator/web-app/app/images/laneboysrc-yt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/configurator/web-app/app/images/laneboysrc-yt.jpg -------------------------------------------------------------------------------- /configurator/web-app/app/index.js: -------------------------------------------------------------------------------- 1 | console.log('GIT VERSION:', VERSION_HASH + VERSION_DIRTY); 2 | 3 | // Pull in Material Design Lite files 4 | require('./stylesheets/material.deep_orange-blue.min.configurator.css'); 5 | require('./stylesheets/dialog-polyfill.css'); 6 | require('./fonts/materialicons.css'); 7 | require('./javascripts/material.min.js'); 8 | 9 | window['saveAs'] = require('./javascripts/FileSaver.min.js'); 10 | window['strftime'] = require('./javascripts/strftime.js'); 11 | window['Sortable'] = require('./javascripts/Sortable.js'); 12 | 13 | // Pull in the service worker registration script 14 | require('./javascripts/service-worker-registration.js'); 15 | 16 | // Pull in our custom stylesheet 17 | require('./stylesheets/styles.css'); 18 | 19 | // Pull in icons and other files that are needed for the various browsers 20 | // Those are not referenced in HTML so we need to drag them in manually. 21 | require('./images/favicon.ico'); 22 | require('./images/laneboysrc-logo-144.png'); 23 | require('./images/laneboysrc-logo-192.png'); 24 | 25 | 26 | // Pull in all application modules 27 | // Actually this pulls in modules/index.js, which in turn requires all app 28 | // modules 29 | require('./modules'); 30 | -------------------------------------------------------------------------------- /configurator/web-app/app/javascripts/FileSaver.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 2 | var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,i=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},a=/constructor/i.test(e.HTMLElement),f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},d="application/octet-stream",s=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,s)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(i){u(i)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,s){if(!s){t=p(t)}var v=this,w=t.type,m=w===d,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&a)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;i(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define([],function(){return saveAs})} -------------------------------------------------------------------------------- /configurator/web-app/app/javascripts/service-worker-registration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Modified by LANE Boys RC 3 | * 4 | * Original copyright notice: 5 | * 6 | * Copyright 2015 Google Inc. All rights reserved. 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | /* eslint-env browser */ 22 | 'use strict'; 23 | 24 | var Utils = require('../modules/utils'); 25 | 26 | 27 | if ('serviceWorker' in navigator) { 28 | // Your service-worker.js *must* be located at the top-level directory relative to your site. 29 | // It won't be able to control pages unless it's located at the same level or higher than them. 30 | // *Don't* register service worker file in, e.g., a scripts/ sub-directory! 31 | // See https://github.com/slightlyoff/ServiceWorker/issues/468 32 | navigator.serviceWorker.register('service-worker.js').then(function(reg) { 33 | // updatefound is fired if service-worker.js changes. 34 | reg.onupdatefound = function() { 35 | // The updatefound event implies that reg.installing is set; see 36 | // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event 37 | var installingWorker = reg.installing; 38 | 39 | installingWorker.onstatechange = function() { 40 | switch (installingWorker.state) { 41 | case 'installed': 42 | if (navigator.serviceWorker.controller) { 43 | // At this point, the old content will have been purged and the fresh content will 44 | // have been added to the cache. 45 | // It's the perfect time to display a "New content is available; please refresh." 46 | // message in the page's interface. 47 | // console.log('New or updated content is available.'); 48 | 49 | let data = { 50 | message: 'A new configurator version is available', 51 | timeout: 5000, 52 | actionHandler: function () { window.location.reload(); }, 53 | actionText: 'Update' 54 | }; 55 | 56 | Utils.showSnackbar(data); 57 | } 58 | else { 59 | // At this point, everything has been precached. 60 | // It's the perfect time to display a "Content is cached for offline use." message. 61 | Utils.showToast('The configurator is now able to operate offline!', 5000); 62 | 63 | } 64 | break; 65 | 66 | case 'redundant': 67 | console.log('The installing service worker became redundant.'); 68 | break; 69 | } 70 | }; 71 | }; 72 | }).catch(function(e) { 73 | console.log('Error during service worker registration:', e); 74 | }); 75 | } 76 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/about.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | 5 | 6 | class About { 7 | constructor() { 8 | const elHash = document.querySelector('#page_about .version .hash'); 9 | const elDate = document.querySelector('#page_about .version .date'); 10 | 11 | elHash.textContent = VERSION_HASH + VERSION_DIRTY; 12 | elDate.textContent = VERSION_DATE; 13 | } 14 | 15 | //************************************************************************* 16 | init() { 17 | Utils.showPage('about'); 18 | } 19 | 20 | //************************************************************************* 21 | back() { 22 | history.back(); 23 | } 24 | } 25 | 26 | window['About'] = new About(); 27 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/edit_curve.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | var MDLHelper = require('./mdl_helper'); 5 | var CurveView = require('./curve_view'); 6 | 7 | 8 | function getCurvePoints(curveType) 9 | { 10 | 11 | let config = Device.MODEL.getConfig(); 12 | let curvePoints = config.TYPES.curve_points_t; 13 | 14 | if (curvePoints.hasOwnProperty(curveType)) { 15 | return curvePoints[curveType]; 16 | } 17 | return []; 18 | } 19 | 20 | 21 | class EditCurve { 22 | constructor () { 23 | this.offset = 0; 24 | this.container = document.querySelector('#app-edit_curve-container'); 25 | this.template = document.querySelector('#app-edit_curve-template').content; 26 | 27 | this.curveViewSVG = document.querySelector('#app-edit_curve-curve_view'); 28 | this.curveView = new CurveView(this.curveViewSVG); 29 | } 30 | 31 | //************************************************************************* 32 | init (params) { 33 | this.offset = params.offset; 34 | 35 | let mdl = new MDLHelper('MODEL', {offset: this.offset}); 36 | let type = Device.MODEL.getItem('MIXER_UNITS_CURVE_TYPE', {offset: this.offset}); 37 | let points = getCurvePoints(type); 38 | 39 | Utils.clearDynamicElements(this.container); 40 | 41 | mdl.setTextContent('#app-edit_curve-curve_type', 'MIXER_UNITS_CURVE_TYPE'); 42 | mdl.setDataURL('#app-edit_curve-curve_type__edit', 43 | ['select_single', 'MODEL', 'MIXER_UNITS_CURVE_TYPE', this.offset]); 44 | 45 | 46 | mdl.setTextContent('#app-edit_curve-curve_smoothing', 'MIXER_UNITS_CURVE_SMOOTHING'); 47 | mdl.setDataURL('#app-edit_curve-curve_smoothing__edit', 48 | ['select_single', 'MODEL', 'MIXER_UNITS_CURVE_SMOOTHING', this.offset]); 49 | 50 | for (let i = 0; i < points.length; i++) { 51 | let options = {offset: this.offset, index: i}; 52 | let mdlSlider = new MDLHelper('MODEL', options); 53 | let t = document.importNode(this.template, true); 54 | 55 | mdlSlider.setTextContentRaw('.app-edit_curve-template--label', points[i], t); 56 | mdlSlider.setSlider('input', 'MIXER_UNITS_CURVE_POINTS', t); 57 | 58 | // Update the curve view whenever a slider changes 59 | t.querySelector('input[type="range"]').addEventListener('input', this._sliderChanged.bind(this)); 60 | 61 | this.container.appendChild(t); 62 | } 63 | 64 | // Show the smoothing checkbox only if there are 3 or more curve points 65 | // (as the smoothing applies to 3-point an up only) 66 | Utils.setVisibility("#app-edit_curve-curve_smoothing__enable", points.length > 2); 67 | 68 | // Set the curve view display to the curve settings 69 | this.curveView.type = type; 70 | this.curveView.smoothing = Device.MODEL.getItemNumber('MIXER_UNITS_CURVE_SMOOTHING', {offset: this.offset}); 71 | this._sliderChanged(); 72 | 73 | Utils.showPage('edit_curve'); 74 | } 75 | 76 | //************************************************************************* 77 | back() { 78 | history.back(); 79 | } 80 | 81 | //************************************************************************* 82 | _sliderChanged() { 83 | const sliders = this.container.querySelectorAll('.can-delete input[type="range"]'); 84 | 85 | let points = []; 86 | Array.from(sliders).forEach(s => points.push(parseInt(s.value))); 87 | 88 | this.curveView.points = points; 89 | } 90 | } 91 | 92 | window['EditCurve'] = new EditCurve(); 93 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/edit_switch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | var MDLHelper = require('./mdl_helper'); 5 | 6 | 7 | class EditSwitch { 8 | constructor () { 9 | this.offset = 0; 10 | } 11 | 12 | //************************************************************************* 13 | init(params) { 14 | this.offset = params.offset; 15 | 16 | let mdl = new MDLHelper('MODEL', {offset: this.offset}); 17 | 18 | mdl.setTextContent('#app-edit_switch-switch', 'MIXER_UNITS_SW_SW'); 19 | mdl.setDataURL('#app-edit_switch-switch__edit', 20 | ['select_single', 'MODEL', 'MIXER_UNITS_SW_SW', this.offset]); 21 | 22 | mdl.setTextContent('#app-edit_switch-comparison', 'MIXER_UNITS_SW_CMP'); 23 | mdl.setDataURL('#app-edit_switch-comparison__edit', 24 | ['select_single', 'MODEL', 'MIXER_UNITS_SW_CMP', this.offset]); 25 | 26 | mdl.setSlider('#app-edit_switch-value', 'MIXER_UNITS_SW_VALUE'); 27 | 28 | Utils.showPage('edit_switch'); 29 | } 30 | 31 | //************************************************************************* 32 | back() { 33 | history.back(); 34 | } 35 | } 36 | 37 | window['EditSwitch'] = new EditSwitch(); 38 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/limits.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | var MDLHelper = require('./mdl_helper'); 5 | 6 | 7 | class Limits { 8 | constructor () { 9 | this.channel = undefined; 10 | } 11 | 12 | //************************************************************************* 13 | init(params) { 14 | this.channel = params.channel; 15 | 16 | let model = Device.MODEL; 17 | 18 | let limits = model.getSchema()['LIMITS']; 19 | let channel_index = model.getNumberOfTypeMember('MIXER_UNITS_DST', this.channel); 20 | 21 | let offset = limits.s * channel_index; 22 | 23 | let mdl = new MDLHelper('MODEL', {offset: offset}); 24 | 25 | mdl.setTextContentRaw('#app-limits-channel', this.channel); 26 | mdl.setSlider('#app-limits-subtrim', 'LIMITS_SUBTRIM'); 27 | mdl.setSlider('#app-limits-ep_l', 'LIMITS_EP_L'); 28 | mdl.setSlider('#app-limits-ep_h', 'LIMITS_EP_H'); 29 | mdl.setSlider('#app-limits-limit_l', 'LIMITS_LIMIT_L'); 30 | mdl.setSlider('#app-limits-limit_h', 'LIMITS_LIMIT_H'); 31 | mdl.setSlider('#app-limits-failsafe', 'LIMITS_FAILSAFE'); 32 | mdl.setSlider('#app-limits-speed', 'LIMITS_SPEED'); 33 | 34 | mdl.setSwitch('#app-limits-invert', 'LIMITS_INVERT'); 35 | 36 | Utils.showPage('limits'); 37 | } 38 | 39 | //************************************************************************* 40 | back() { 41 | history.back(); 42 | } 43 | } 44 | 45 | window['Limits'] = new Limits(); 46 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | 5 | 6 | class Main { 7 | constructor() { 8 | this.transportCard = document.querySelector('#main-transport_card'); 9 | this.transport = document.querySelector('#main-transport'); 10 | } 11 | 12 | //************************************************************************* 13 | init() { 14 | Device.MODEL = undefined; 15 | Device.TX = undefined; 16 | Device.UNDO = undefined; 17 | Device.disableCommunication(); 18 | 19 | // Hide the transport selection switch when WebUSB is not supported by 20 | // this browser. The default of the switch is "Wi-Fi". 21 | if (typeof navigator.usb === 'undefined') { 22 | Utils.hide(this.transportCard); 23 | } 24 | 25 | Utils.showPage('main'); 26 | } 27 | 28 | //************************************************************************* 29 | connect(event) { 30 | Utils.cancelBubble(event); 31 | 32 | if (this.transport.checked) { 33 | Device.setTransport(WebusbTransport); 34 | } 35 | else { 36 | Device.setTransport(WebsocketTransport); 37 | } 38 | location.hash = Utils.buildURL(['device_list']); 39 | } 40 | 41 | //************************************************************************* 42 | about(event) { 43 | Utils.cancelBubble(event); 44 | location.hash = Utils.buildURL(['about']); 45 | this._closeDrawer(); 46 | } 47 | 48 | //************************************************************************* 49 | settings(event) { 50 | Utils.cancelBubble(event); 51 | location.hash = Utils.buildURL(['settings']); 52 | this._closeDrawer(); 53 | } 54 | 55 | //************************************************************************* 56 | models(event) { 57 | Utils.cancelBubble(event); 58 | location.hash = Utils.buildURL(['model_list']); 59 | this._closeDrawer(); 60 | } 61 | 62 | //************************************************************************* 63 | transmitters(event) { 64 | Utils.cancelBubble(event); 65 | location.hash = Utils.buildURL(['transmitter_list']); 66 | this._closeDrawer(); 67 | } 68 | 69 | //************************************************************************* 70 | _closeDrawer() { 71 | // We can only do this after MDL has initialized, otherwise the drawer_button 72 | // does not exist 73 | let drawerButton = document.querySelector('#page_main .mdl-layout__drawer-button'); 74 | 75 | // Close the drawer only after a short delay as to not interfere with 76 | // loading of the new page 77 | window.setTimeout(() => { 78 | drawerButton.click(); 79 | }, 50); 80 | } 81 | } 82 | 83 | window['Main'] = new Main(); 84 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/mixer_unit.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | var MDLHelper = require('./mdl_helper'); 5 | var CurveView = require('./curve_view'); 6 | 7 | 8 | class MixerUnit { 9 | constructor() { 10 | this.offset = 0; 11 | 12 | this.curveViewSVG = document.querySelector('#app-mixer_unit-curve_view'); 13 | this.curveView = new CurveView(this.curveViewSVG, true); 14 | 15 | document.querySelector('#app-mixer_unit-offset').addEventListener('input', this._offsetChanged.bind(this)); 16 | document.querySelector('#app-mixer_unit-scalar').addEventListener('input', this._scalarChanged.bind(this)); 17 | } 18 | 19 | //************************************************************************* 20 | init(params) { 21 | this.index = params.index; 22 | 23 | let mixer_units = Device.MODEL.getSchema()['MIXER_UNITS']; 24 | this.offset = mixer_units.s * this.index; 25 | 26 | let mdl = new MDLHelper('MODEL', {offset: this.offset}); 27 | 28 | mdl.setTextContent('#app-mixer_unit-src', 'MIXER_UNITS_SRC'); 29 | mdl.setSwitch('#app-mixer_unit-invert_source', 'MIXER_UNITS_INVERT_SOURCE'); 30 | mdl.setTextContent('#app-mixer_unit-dst', 'MIXER_UNITS_DST'); 31 | mdl.setTextContent('#app-mixer_unit-op', 'MIXER_UNITS_OP'); 32 | mdl.setSwitch('#app-mixer_unit-apply_trim', 'MIXER_UNITS_APPLY_TRIM'); 33 | mdl.setTextContent('#app-mixer_unit-curve', 'MIXER_UNITS_CURVE_TYPE'); 34 | mdl.setTextContent('#app-mixer_unit-sw', 'MIXER_UNITS_SW_SW'); 35 | 36 | mdl.setDataURL('#app-mixer_unit-curve__edit', ['edit_curve', this.offset]); 37 | mdl.setDataURL('#app-mixer_unit-sw__edit', ['edit_switch', this.offset]); 38 | mdl.setDataURL('#app-mixer_unit-src__edit', 39 | ['select_single', 'MODEL', 'MIXER_UNITS_SRC', this.offset]); 40 | mdl.setDataURL('#app-mixer_unit-dst__edit', 41 | ['select_single', 'MODEL', 'MIXER_UNITS_DST', this.offset]); 42 | mdl.setDataURL('#app-mixer_unit-op__edit', 43 | ['select_single', 'MODEL', 'MIXER_UNITS_OP', this.offset]); 44 | 45 | mdl.setSlider('#app-mixer_unit-scalar', 'MIXER_UNITS_SCALAR'); 46 | mdl.setSlider('#app-mixer_unit-offset', 'MIXER_UNITS_OFFSET'); 47 | 48 | 49 | // Set the curve view display to the curve settings 50 | this.curveView.type = Device.MODEL.getItem('MIXER_UNITS_CURVE_TYPE', {offset: this.offset});; 51 | this.curveView.smoothing = Device.MODEL.getItemNumber('MIXER_UNITS_CURVE_SMOOTHING', {offset: this.offset}); 52 | this.curveView.points = Device.MODEL.getItemNumber('MIXER_UNITS_CURVE_POINTS', {offset: this.offset}); 53 | this._offsetChanged(); 54 | this._scalarChanged(); 55 | 56 | Utils.showPage('mixer_unit'); 57 | } 58 | 59 | //************************************************************************* 60 | back() { 61 | history.back(); 62 | } 63 | 64 | //************************************************************************* 65 | delete(event) { 66 | Utils.cancelBubble(event); 67 | 68 | Mixer.deleteMixerUnit(this.index); 69 | history.back(); 70 | } 71 | 72 | //************************************************************************* 73 | _offsetChanged() { 74 | this.curveView.offset = parseInt(document.querySelector('#app-mixer_unit-offset').value); 75 | } 76 | 77 | //************************************************************************* 78 | _scalarChanged() { 79 | this.curveView.scalar = parseInt(document.querySelector('#app-mixer_unit-scalar').value); 80 | } 81 | } 82 | 83 | window['MixerUnit'] = new MixerUnit(); 84 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/model_details.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | var MDLHelper = require('./mdl_helper'); 5 | 6 | 7 | class ModelDetails { 8 | constructor() { 9 | // Nothing to do 10 | } 11 | 12 | //************************************************************************* 13 | init(params) { 14 | let mdl = new MDLHelper('MODEL'); 15 | 16 | mdl.setTextfield('#app-model_details-name', 'NAME'); 17 | mdl.setDataURL('#app-model_details-mixer', ['mixer']); 18 | mdl.setDataURL('#app-model_details-rf_protocol', ['rf_protocol']); 19 | mdl.setIcon('#app-model_details-icon'); 20 | 21 | // Show/hide the menu depending on whether tx_uuid is undefined 22 | Utils.setVisibility('.app-model_details--transmitter', params.tx); 23 | Utils.setVisibility('.app-model_details--no-transmitter', !params.tx); 24 | 25 | Utils.showPage('model_details'); 26 | } 27 | 28 | //************************************************************************* 29 | back() { 30 | history.back(); 31 | } 32 | 33 | //************************************************************************* 34 | changeModel(event) { 35 | Utils.cancelBubble(event); 36 | location.hash = Utils.buildURL(['model_list']); 37 | } 38 | 39 | //************************************************************************* 40 | selectIcon(event) { 41 | Utils.cancelBubble(event); 42 | console.log('ModelDetails.selectIcon()') 43 | 44 | location.hash = Utils.buildURL(['select_icon', 'MODEL', 'TAG', 0]); 45 | } 46 | 47 | //************************************************************************* 48 | deleteModel(event) { 49 | Utils.cancelBubble(event); 50 | 51 | ModelList.deleteModel(Device.MODEL); 52 | Device.MODEL = undefined; 53 | history.back(); 54 | } 55 | 56 | //************************************************************************* 57 | configureTransmitter(event) { 58 | Utils.cancelBubble(event); 59 | location.hash = Utils.buildURL(['transmitter_details']); 60 | } 61 | } 62 | 63 | window['ModelDetails'] = new ModelDetails(); 64 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/select_icon.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | var MDLHelper = require('./mdl_helper'); 5 | 6 | 7 | class SelectIcon { 8 | constructor() { 9 | this.devName = undefined; 10 | this.item = undefined; 11 | this.offset = 0; 12 | 13 | this.template = document.querySelector('#app-select_icon-template').content; 14 | this.list = document.querySelector('#app-select_icon-list'); 15 | } 16 | 17 | //************************************************************************* 18 | init(params) { 19 | this.devName = params.devName; 20 | this.item = params.item; 21 | this.offset = parseInt(params.offset); 22 | 23 | let mdl = new MDLHelper(this.devName); 24 | 25 | mdl.setTextContent('#app-select_icon-name', 'NAME'); 26 | 27 | // Ged rid of existing elements 28 | Utils.clearDynamicElements(this.list); 29 | 30 | let device = Device[this.devName]; 31 | let current_choice = device.getItem(this.item, {offset: this.offset}); 32 | let choices = mdl.icons; 33 | 34 | let i = 0; 35 | for (let entry in choices) { 36 | if (!choices.hasOwnProperty(entry)) { 37 | continue; 38 | } 39 | 40 | let t = document.importNode(this.template, true); 41 | t.querySelector('i').textContent = choices[entry]; 42 | t.querySelector('input').id = 'app-select_icon__item' + i; 43 | t.querySelector('input').value = entry; 44 | t.querySelector('label').setAttribute('for', 'app-select_icon__item' + i); 45 | t.querySelector('input').checked = (parseInt(entry) === current_choice); 46 | this.list.appendChild(t); 47 | ++i; 48 | } 49 | 50 | Utils.showPage('select_icon'); 51 | } 52 | 53 | //************************************************************************* 54 | back() { 55 | history.back(); 56 | } 57 | 58 | //************************************************************************* 59 | acceptChoice(event) { 60 | Utils.cancelBubble(event); 61 | 62 | let list = document.querySelector('#app-select_icon-list'); 63 | let value = list.querySelector('input[type="radio"]:checked').value; 64 | 65 | Device[this.devName].setItem(this.item, value, {offset: this.offset}); 66 | history.go(-1); 67 | } 68 | } 69 | 70 | window['SelectIcon'] = new SelectIcon(); 71 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/settings.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | 5 | 6 | class Settings { 7 | constructor() { 8 | this.restoreLog = document.querySelector('#settings-restore'); 9 | document.querySelector('#settings-restore__input').onchange = this.restore.bind(this); 10 | } 11 | 12 | //************************************************************************* 13 | init() { 14 | Utils.removeChildren(this.restoreLog); 15 | Utils.showPage('settings'); 16 | } 17 | 18 | //************************************************************************* 19 | back() { 20 | history.back(); 21 | } 22 | 23 | //************************************************************************* 24 | backup(event) { 25 | Utils.cancelBubble(event); 26 | 27 | let entries = []; 28 | 29 | function collect(cursor) { 30 | if (cursor) { 31 | let entry = cursor.value; 32 | 33 | // Convert the Uint8Array into a regular array to prevent JSON from 34 | // storing it as an object, which bloats the JSON 35 | entry.data = Array.from(entry.data); 36 | 37 | entries.push(entry); 38 | cursor.continue(); 39 | } 40 | else { 41 | let json = JSON.stringify(entries, null, 2); 42 | 43 | let blob = new Blob([json], {type: 'application/json'}); 44 | const now = strftime('%Y%m%dT%H%M'); 45 | window.saveAs.saveAs(blob, `headless-tx-backup-${now}.json`); 46 | } 47 | } 48 | 49 | Database.listEntries(collect); 50 | } 51 | 52 | //************************************************************************* 53 | restore(event) { 54 | const input = event.target; 55 | if (input.files.length < 1) { 56 | return; 57 | } 58 | 59 | Utils.removeChildren(this.restoreLog); 60 | const self = this; 61 | const reader = new FileReader(); 62 | 63 | reader.onload = function (e) { 64 | let data = JSON.parse(e.target.result); 65 | data.forEach(entry => { 66 | if ('configVersion' in entry && 67 | 'schemaName' in entry && 68 | 'data' in entry && 69 | 'lastChanged' in entry && 70 | 'uuid' in entry ) { 71 | 72 | Database.getEntry(entry.uuid, existingEntry => { 73 | if (existingEntry && existingEntry.lastChanged > entry.lastChanged) { 74 | const logEntry = document.createElement('DIV'); 75 | logEntry.textContent = `Existing entry for ${entry.schemaName} ${entry.uuid} is newer, not overwriting`; 76 | self.restoreLog.appendChild(logEntry); 77 | } 78 | else { 79 | const logEntry = document.createElement('DIV'); 80 | logEntry.textContent = `Adding ${entry.schemaName} ${entry.uuid} to database`; 81 | self.restoreLog.appendChild(logEntry); 82 | 83 | // Convert the regular array into an Uint8Array 84 | entry.data = Uint8Array.from(entry.data); 85 | 86 | Database.setEntry(entry); 87 | } 88 | }); 89 | 90 | } 91 | }); 92 | }; 93 | 94 | reader.readAsText(input.files[0]); 95 | } 96 | 97 | } 98 | 99 | window['Settings'] = new Settings(); 100 | -------------------------------------------------------------------------------- /configurator/web-app/app/modules/transmitter_details.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Utils = require('./utils'); 4 | var MDLHelper = require('./mdl_helper'); 5 | 6 | 7 | function passphraseFormatter(passphrase) { 8 | let s = passphrase.toString(); 9 | 10 | while (s.length < 4) { 11 | s = '0' + s; 12 | } 13 | return s; 14 | } 15 | 16 | 17 | class TransmitterDetails { 18 | constructor() { 19 | // Nothing to do 20 | } 21 | 22 | //************************************************************************* 23 | init(params) { 24 | let mdl = new MDLHelper('TX'); 25 | 26 | mdl.setTextfield('#app-transmitter_details-name', 'NAME'); 27 | mdl.setSlider('#app-transmitter_details-trim_range', 'TRIM_RANGE'); 28 | mdl.setSlider('#app-transmitter_details-trim_step_size', 'TRIM_STEP_SIZE'); 29 | mdl.setSlider('#app-transmitter_details-meter', 'METER_PWM_PERCENT'); 30 | 31 | mdl.formatter = passphraseFormatter; 32 | mdl.setTextfield('#app-transmitter_details-passphrase', 'PASSPHRASE'); 33 | 34 | mdl.setDataURL('#app-transmitter_details-hardware', ['hardware_inputs']); 35 | mdl.setDataURL('#app-transmitter_details-logical', ['logical_inputs']); 36 | 37 | // Only show the passphrase card when we are connected to a transmitter. 38 | Utils.setVisibility('#app-transmitter_details-passphrase-card', params.model); 39 | 40 | Utils.showPage('transmitter_details'); 41 | } 42 | 43 | //************************************************************************* 44 | back() { 45 | history.back(); 46 | } 47 | 48 | //************************************************************************* 49 | delete(event) { 50 | Utils.cancelBubble(event); 51 | 52 | TransmitterList.deleteTransmitter(Device.TX); 53 | Device.TX = undefined; 54 | history.back(); 55 | } 56 | } 57 | 58 | window['TransmitterDetails'] = new TransmitterDetails(); 59 | -------------------------------------------------------------------------------- /configurator/web-app/app/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Headless RC Transmitter configuration tool", 3 | "short_name": "Headless TX", 4 | "description": "A tool to configure the headless radio control transmitters designed by LANE Boys RC", 5 | "start_url": "index.html", 6 | "display": "standalone", 7 | "theme_color": "rgb(255,87,34)", 8 | "background_color": "rgb(255,87,34)", 9 | "icons": [ 10 | { 11 | "src": "laneboysrc-logo-192.png", 12 | "sizes": "192x192", 13 | "type": "image/png" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /configurator/web-app/app/stylesheets/dialog-polyfill.css: -------------------------------------------------------------------------------- 1 | dialog { 2 | position: absolute; 3 | left: 0; right: 0; 4 | width: -moz-fit-content; 5 | width: -webkit-fit-content; 6 | width: fit-content; 7 | height: -moz-fit-content; 8 | height: -webkit-fit-content; 9 | height: fit-content; 10 | margin: auto; 11 | border: solid; 12 | padding: 1em; 13 | background: white; 14 | color: black; 15 | display: none; 16 | } 17 | 18 | dialog[open] { 19 | display: block; 20 | } 21 | 22 | dialog + .backdrop { 23 | position: fixed; 24 | top: 0; right: 0; bottom: 0; left: 0; 25 | background: rgba(0,0,0,0.1); 26 | } 27 | 28 | /* for small devices, modal dialogs go full-screen */ 29 | @media screen and (max-width: 540px) { 30 | dialog[_polyfill_modal] { /* TODO: implement */ 31 | top: 0; 32 | width: auto; 33 | margin: 1em; 34 | } 35 | } 36 | 37 | ._dialog_overlay { 38 | position: fixed; 39 | top: 0; right: 0; bottom: 0; left: 0; 40 | } -------------------------------------------------------------------------------- /configurator/web-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "configurator-web-app", 3 | "version": "0.0.1", 4 | "description": "A browser based configuration tool for the Headless TX", 5 | "repository": "https://github.com/laneboysrc/rc-headless-transmitter.git", 6 | "author": "laneboysrc@gmail.com", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "webpack", 10 | "production": "webpack", 11 | "dev": "webpack-dev-server", 12 | "development": "webpack-dev-server", 13 | "start": "webpack-dev-server" 14 | }, 15 | "readmeFilename": "README.md", 16 | "devDependencies": { 17 | "babel-core": "^6.26.3", 18 | "babel-loader": "^6.4.1", 19 | "babel-polyfill": "^6.26.0", 20 | "babel-preset-env": "^1.7.0", 21 | "clean-webpack-plugin": "^0.1.10", 22 | "css-loader": "^0.23.1", 23 | "dialog-polyfill": "^0.4.3", 24 | "extract-text-webpack-plugin": "^1.0.1", 25 | "file-loader": "^0.9.0", 26 | "html-loader": "^0.4.3", 27 | "html-webpack-plugin": "^2.22.0", 28 | "nunjucks-html-loader": "^1.0.0", 29 | "style-loader": "^0.13.2", 30 | "sw-precache": "^4.2.1", 31 | "sw-precache-webpack-plugin": "^0.5.1", 32 | "test-data": "file:../common-modules/test-data", 33 | "url-loader": "^0.5.7", 34 | "webpack": "^1.13.3", 35 | "webpack-dev-server": "^1.14.1", 36 | "webpack-merge": "^0.14.0", 37 | "webpack-validator": "^2.2.3" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs/battery-life-test/.gitignore: -------------------------------------------------------------------------------- 1 | *pyc -------------------------------------------------------------------------------- /docs/battery-life-test/2016-06-04_Sony800mAh-3.63V-to-shutoff.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/battery-life-test/2016-06-04_Sony800mAh-3.63V-to-shutoff.ods -------------------------------------------------------------------------------- /docs/battery-life-test/2016-06-04_Sony800mAh-3.63V-to-shutoff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/battery-life-test/2016-06-04_Sony800mAh-3.63V-to-shutoff.pdf -------------------------------------------------------------------------------- /docs/battery-life-test/2016-06-05-Sony800mAh-full-to-shutoff.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/battery-life-test/2016-06-05-Sony800mAh-full-to-shutoff.ods -------------------------------------------------------------------------------- /docs/battery-life-test/2016-06-05-Sony800mAh-full-to-shutoff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/battery-life-test/2016-06-05-Sony800mAh-full-to-shutoff.pdf -------------------------------------------------------------------------------- /docs/battery-life-test/scpi/README.md: -------------------------------------------------------------------------------- 1 | python-scpi 2 | =========== 3 | 4 | Since all the other wrappers either require VISA binary or are not generic (and do not implement the device I need) 5 | 6 | Basic idea here is to make transport-independent command sender/parser and a device baseclass that implements the common SCPI commands 7 | 8 | A device specific implementation can then add the device-specific commands. 9 | 10 | Pro tip for thos wishing to work on the code 11 | 12 | ## TODO 13 | 14 | ### Check Carrier-Detect for RS232 transport 15 | 16 | in the RS232 transport check getCD to make sure the device is present before doing anything. 17 | CTS can also be checked even if hw flow control is not in use. 18 | 19 | Basically wait for it for X seconds and abort if not found 20 | 21 | ### ZMQ/DBus signals and helpers 22 | 23 | For remote-control and sharing access to the resource 24 | -------------------------------------------------------------------------------- /docs/battery-life-test/scpi/__init__.py: -------------------------------------------------------------------------------- 1 | """SCPI module, the scpi class implements the base command set, devices may extend it. transports are separate from devices 2 | (so you can use for example hp6632b with either serial port or GPIB)""" 3 | from .scpi import scpi, scpi_device 4 | from .errors import * 5 | -------------------------------------------------------------------------------- /docs/battery-life-test/scpi/devices/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/battery-life-test/scpi/devices/__init__.py -------------------------------------------------------------------------------- /docs/battery-life-test/scpi/errors/__init__.py: -------------------------------------------------------------------------------- 1 | """SCPI module specific errors""" 2 | 3 | from exceptions import RuntimeError 4 | 5 | 6 | class TimeoutError(RuntimeError): 7 | def __init__(self, command, time, *args, **kwargs): 8 | self.command = command 9 | self.time = time 10 | super(TimeoutError, self).__init__(str(self), *args, **kwargs) 11 | 12 | def __str__(self): 13 | return "'%s' timed out after %f seconds" % (self.command, self.time) 14 | 15 | class CommandError(RuntimeError): 16 | def __init__(self, command, code, message, *args, **kwargs): 17 | self.command = command 18 | self.code = code 19 | self.message = message 20 | super(CommandError, self).__init__(str(self), *args, **kwargs) 21 | 22 | def __str__(self): 23 | return "'%s' returned error %d: %s" % (self.command, self.code, self.message) 24 | 25 | -------------------------------------------------------------------------------- /docs/battery-life-test/scpi/transports/__init__.py: -------------------------------------------------------------------------------- 1 | """Transport layers for the SCPI module""" 2 | from .baseclass import transports_base as base 3 | from .rs232 import transports_rs232 as rs232 4 | -------------------------------------------------------------------------------- /docs/battery-life-test/scpi/transports/baseclass.py: -------------------------------------------------------------------------------- 1 | """Baseclass for all the transports, if common methods are needed they will be defined here 2 | 3 | All transports must define certain basic methods (check all the raise NotImplementedError) 4 | """ 5 | 6 | from exceptions import NotImplementedError 7 | from exceptions import RuntimeError 8 | 9 | 10 | class transports_base(object): 11 | def __init__(self): 12 | """Initializes a transport""" 13 | pass 14 | 15 | def quit(self, command): 16 | """Must shutdown all background threads (if any)""" 17 | raise NotImplementedError() 18 | 19 | def set_message_callback(self, callback): 20 | self.message_received = callback 21 | 22 | def send_command(self, command): 23 | """Sends a complete command to the device, line termination etc is handled by the transport""" 24 | raise NotImplementedError() 25 | 26 | def message_received(self, message): 27 | """Default message callback raises error""" 28 | raise RuntimeError("Message callback not set") 29 | 30 | def incoming_data(self): 31 | """Check whether we still have inbound data, must return boolean""" 32 | raise NotImplementedError() 33 | 34 | def abort_command(self): 35 | """Send the "device clear" command to abort a running command""" 36 | raise NotImplementedError() 37 | -------------------------------------------------------------------------------- /docs/configurator-sequence-diagram.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/configurator-sequence-diagram.odg -------------------------------------------------------------------------------- /docs/configurator-sequence-diagram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/configurator-sequence-diagram.pdf -------------------------------------------------------------------------------- /docs/configurator-ui-flow.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/configurator-ui-flow.odg -------------------------------------------------------------------------------- /docs/configurator-ui-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/configurator-ui-flow.png -------------------------------------------------------------------------------- /docs/new-rf-protocol.md: -------------------------------------------------------------------------------- 1 | ## RF protocol for more than 3 channels 2 | 3 | The HK310 protocol can only support up to 3 channels. Since we now have the 4 | possibility to build our own transmitter and receiver, we can change the 5 | protocol to support more channels. We still want to use the nRF based RF 6 | solution, since it is robust, cheap and we know it well. 7 | 8 | 9 | ## HK300 / HK310 / X3S RF protocol 10 | 11 | * 20 hop channels 12 | * 5 byte address 13 | * 3 servo channels 14 | * Stick data sent every 5 ms 15 | * Hop frequency changed every 5 ms 16 | * Failsafe sent 17 | 18 | 19 | ## Requirements of the new protocol 20 | 21 | * Based on HK300 protocol 22 | * At least 8 channels 23 | * signed 12 bit, corresponding to 476..1500..2523 us (500 ns resolution) 24 | - 8 channels, signed 12 bits (0 corresponds to 1500 us servo output == center) 25 | - So a packet would be 12 Bytes of data, plus one frame identifier 26 | - More channels can be added by introducing multiple frames. The higher channels could be sent at a 27 | slower update rate 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/transmitter-configurator-phone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/transmitter-configurator-phone.jpg -------------------------------------------------------------------------------- /docs/transmitter-hardware-overview.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/docs/transmitter-hardware-overview.ods -------------------------------------------------------------------------------- /transmitter/electronics/.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.kicad_pcb-bak 3 | -------------------------------------------------------------------------------- /transmitter/electronics/BOM.txt: -------------------------------------------------------------------------------- 1 | BT1 BATTERY Li-Ion 3.7V 800mAh 2 | C1 1u 3 | C2 100nF 4 | K1 CONN_3 0.1" pin header 5 | K2 CONN_3 0.1" pin header 6 | K3 CONN_3 0.1" pin header 7 | K4 CONN_3 0.1" pin header 8 | K5 CONN_3 0.1" pin header 9 | K6 CONN_3 0.1" pin header 10 | K7 CONN_3 0.1" pin header 11 | K8 CONN_3 0.1" pin header 12 | K9 CONN_3 0.1" pin header 13 | K10 CONN_3 0.1" pin header 14 | K11 CONN_3 0.1" pin header 15 | P1 Charge 0.1" pin header 16 | P2 LED 0.1" pin header 17 | P3 CONN_2 0.1" pin header 18 | P4 CONN_2 0.1" pin header 19 | P5 CONN_4 0.1" pin header 20 | Q1 BC548 21 | R1 1k 22 | R2 22k 23 | R3 33k 24 | R4 1k 25 | SP1 Passive Piezo buzzer https://www.aliexpress.com/item/Black-Plastic-14-x-7mm-2-Pins-Passive-Piezo-Buzzer-AC1-3V-Piezo-Transducer-with-Flying/32723376720.html 26 | SW1 ON/OFF switch 27 | U1 LIPO_CHARGER_+_PROTECTION https://www.aliexpress.com/item/New-2Pcs-lot-5V-1A-Micro-USB-18650-Lithium-Battery-Charging-Board-Charger-Module-Protection-Dual/32230225249.html 28 | U2 STM32F103C8T6_DEV_BOARD https://www.aliexpress.com/item/1pcs-STM32F103C8T6-ARM-STM32-Minimum-System-Development-Board-Module-For-arduino/32478120209.html 29 | U3 NRF24L01P https://www.aliexpress.com/item/1100-Meter-Long-Distance-NRF24L01-PA-LNA-NRF24L01P-Wireless-Transceiver-Communication-Module-with-antenna-DC-3/32597272885.html 30 | -------------------------------------------------------------------------------- /transmitter/electronics/README.md: -------------------------------------------------------------------------------- 1 | This folder contains the schematics and layout of the *headless transmitter* hardware. 2 | 3 | [BOM.txt](BOM.txt) contains a list of all components. 4 | 5 | The project is made using [KiCad](http://kicad-pcb.org/). For convenience the schematics are also available in [PDF format](stm32f1-nrf24l01-transmitter.pdf). 6 | 7 | Note that the PCB designs are not actual circuit board designs, but rather meant to be built on a [Perfboard](https://en.wikipedia.org/wiki/Perfboard). 8 | 9 | There are different variants that we adapted to suit different transmitters we modified. 10 | -------------------------------------------------------------------------------- /transmitter/electronics/WLA.pretty/LiPo_charger_and_protection.kicad_mod: -------------------------------------------------------------------------------- 1 | (module LiPo_charger_and_protection (layer F.Cu) (tedit 5701FD50) 2 | (fp_text reference U1 (at 0 0) (layer F.SilkS) 3 | (effects (font (size 1 1) (thickness 0.15))) 4 | ) 5 | (fp_text value LIPO_CHARGER_+_PROTECTION (at 0 7) (layer F.SilkS) hide 6 | (effects (font (size 1 1) (thickness 0.15))) 7 | ) 8 | (fp_text user - (at -9.5 7) (layer F.SilkS) 9 | (effects (font (size 1 1) (thickness 0.15))) 10 | ) 11 | (fp_text user + (at -9.5 -7) (layer F.SilkS) 12 | (effects (font (size 1 1) (thickness 0.15))) 13 | ) 14 | (fp_text user OUT- (at 8 7) (layer F.SilkS) 15 | (effects (font (size 1 1) (thickness 0.15))) 16 | ) 17 | (fp_text user OUT+ (at 8 -7) (layer F.SilkS) 18 | (effects (font (size 1 1) (thickness 0.15))) 19 | ) 20 | (fp_text user B- (at 9 4) (layer F.SilkS) 21 | (effects (font (size 1 1) (thickness 0.15))) 22 | ) 23 | (fp_text user B+ (at 9 -4) (layer F.SilkS) 24 | (effects (font (size 1 1) (thickness 0.15))) 25 | ) 26 | (fp_line (start 13 -6.5) (end 13 6.5) (layer F.SilkS) (width 0.15)) 27 | (fp_line (start 13 8.5) (end 14 8.5) (layer F.SilkS) (width 0.15)) 28 | (fp_line (start 14 8.5) (end 14 6.5) (layer F.SilkS) (width 0.15)) 29 | (fp_line (start 14 6.5) (end 13 6.5) (layer F.SilkS) (width 0.15)) 30 | (fp_line (start 13 -8.5) (end 14 -8.5) (layer F.SilkS) (width 0.15)) 31 | (fp_line (start 14 -8.5) (end 14 -6.5) (layer F.SilkS) (width 0.15)) 32 | (fp_line (start 14 -6.5) (end 13 -6.5) (layer F.SilkS) (width 0.15)) 33 | (fp_line (start -13 -4) (end -14 -4) (layer F.SilkS) (width 0.15)) 34 | (fp_line (start -14 -4) (end -14 4) (layer F.SilkS) (width 0.15)) 35 | (fp_line (start -14 4) (end -13 4) (layer F.SilkS) (width 0.15)) 36 | (fp_line (start -13 -8.5) (end -13 8.5) (layer F.SilkS) (width 0.15)) 37 | (fp_line (start -13 8.5) (end 13 8.5) (layer F.SilkS) (width 0.15)) 38 | (fp_line (start 13 -8.5) (end -13 -8.5) (layer F.SilkS) (width 0.15)) 39 | (pad 3 thru_hole rect (at 11.43 -6.985) (size 2.54 2.54) (drill 0.9) (layers *.Cu *.Mask F.SilkS)) 40 | (pad 1 thru_hole rect (at 11.43 -3.81) (size 2.54 2.54) (drill 0.9) (layers *.Cu *.Mask F.SilkS)) 41 | (pad 4 thru_hole rect (at 11.43 6.985) (size 2.54 2.54) (drill 0.9) (layers *.Cu *.Mask F.SilkS)) 42 | (pad 2 thru_hole rect (at 11.43 3.81) (size 2.54 2.54) (drill 0.9) (layers *.Cu *.Mask F.SilkS)) 43 | (pad 5 thru_hole rect (at -11.43 -6.985) (size 2.54 2.54) (drill 0.9) (layers *.Cu *.Mask F.SilkS)) 44 | (pad 6 thru_hole rect (at -11.43 6.985) (size 2.54 2.54) (drill 0.9) (layers *.Cu *.Mask F.SilkS)) 45 | ) 46 | -------------------------------------------------------------------------------- /transmitter/electronics/airtronics-meter.md: -------------------------------------------------------------------------------- 1 | # Airtronics analog meter 2 | 3 | R = 670 Ohm 4 | Full scale: 400mV / 600uA 5 | Orange to full: 200mV / 300uA 6 | Red to orange: 140mV / 200uA 7 | 8 | The needle moves until 500mV (needle at far right), which should be our design maximum. 9 | 10 | So we need a voltage divider from 3.3V to 0.5V, with the bottom resistor being 670 Ohm 11 | 12 | 3.3 0.5 3.3 * 670 13 | ------- = ------- => --------- - 670 = x => x = 3752 ohm ~ 3900 Ohm 14 | x + 670 670 0.5 15 | 16 | If we use 3k9, we get a maximum voltage of 483 mV, which is plenty. -------------------------------------------------------------------------------- /transmitter/electronics/airtronics/fp-info-cache: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /transmitter/electronics/airtronics/stm32f1-nrf24l01-transmitter-airtronics.pro: -------------------------------------------------------------------------------- 1 | update=Fri Jan 17 14:19:35 2020 2 | version=1 3 | last_client=kicad 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [cvpcb] 9 | version=1 10 | NetIExt=net 11 | [pcbnew] 12 | version=1 13 | PageLayoutDescrFile= 14 | LastNetListRead=stm32f1-nrf24l01-transmitter.net 15 | PadDrill=0.6 16 | PadDrillOvalY=0.6 17 | PadSizeH=1.5 18 | PadSizeV=1.5 19 | PcbTextSizeV=1.5 20 | PcbTextSizeH=1.5 21 | PcbTextThickness=0.3 22 | ModuleTextSizeV=1 23 | ModuleTextSizeH=1 24 | ModuleTextSizeThickness=0.15 25 | SolderMaskClearance=0 26 | SolderMaskMinWidth=0 27 | DrawSegmentWidth=0.2 28 | BoardOutlineThickness=0.09999999999999999 29 | ModuleOutlineThickness=0.15 30 | [eeschema] 31 | version=1 32 | LibDir= 33 | -------------------------------------------------------------------------------- /transmitter/electronics/airtronics/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (lib (name stm32f1-nrf24l01-transmitter-rescue)(type Legacy)(uri ${KIPRJMOD}/stm32f1-nrf24l01-transmitter-rescue.lib)(options "")(descr "")) 3 | ) 4 | -------------------------------------------------------------------------------- /transmitter/electronics/futaba-meter.md: -------------------------------------------------------------------------------- 1 | # Futaba FP-2F analog meter 2 | 3 | R = 400 Ohm 4 | Full scale: 450 mV / 5 | Red to orange: 225 mV / 6 | 7 | The needle moves until 480mV (needle at far right), which should be our design maximum. 8 | 9 | So we need a voltage divider from 3.3V to 0.48V, with the bottom resistor being 400 Ohm 10 | 11 | 3.3 0.48 3.3 * 400 12 | ------- = ------- => --------- - 400 = x => x = 2350 ohm ~ 2200 Ohm 13 | x + 400 400 0.48 14 | 15 | If we use 2k2, we get a maximum voltage of 508 mV, which is plenty. -------------------------------------------------------------------------------- /transmitter/electronics/heathkit-pcb-connection-diagram.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/electronics/heathkit-pcb-connection-diagram.pdf -------------------------------------------------------------------------------- /transmitter/electronics/pcb/.gitignore: -------------------------------------------------------------------------------- 1 | *.sch-bak 2 | -------------------------------------------------------------------------------- /transmitter/electronics/pcb/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (lib (name WLA)(type KiCad)(uri /home/laneboysrc/projects/rc-headless-transmitter/transmitter/electronics/WLA.pretty)(options "")(descr "")) 3 | (lib (name tact-switch)(type KiCad)(uri ${KIPRJMOD}/tact-switch.pretty)(options "")(descr "")) 4 | ) 5 | -------------------------------------------------------------------------------- /transmitter/electronics/pcb/measurements.txt: -------------------------------------------------------------------------------- 1 | Graupner Varioprop E8 2 | 3 | s2 4 | 5 | s1 6 | 7 | 8 | s3 9 | 10 | x: 80+ 11 | y: 48 12 | s2/s3 to edge: 24 13 | s2.y - s3.y: 39 14 | s1.x - s2/s3.x: 60 15 | 16 | 17 | 18 | 19 | Esky 20 | 21 | s1 T s2 s3 22 | 23 | 24 | 25 | 26 | s4 s5 27 | 28 | x: 104+ 29 | y: 41 30 | s1.y/s2.y/s3.y to edge: 3 31 | s3.x/s5.x to edge: 9 32 | s1.x - s2.x: 40 33 | s2.x - s3.x: 55 34 | s2.y - s4.y: 28 35 | t.x= 12 36 | t.y = 3 37 | t.y2 = 12 38 | t.x(right) - s2.x = 18 39 | 40 | 41 | -------------------------------------------------------------------------------- /transmitter/electronics/pcb/rc-headless-tx-gerber.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/electronics/pcb/rc-headless-tx-gerber.zip -------------------------------------------------------------------------------- /transmitter/electronics/pcb/stm32f1-nrf24l01-transmitter-pcb.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/electronics/pcb/stm32f1-nrf24l01-transmitter-pcb.pdf -------------------------------------------------------------------------------- /transmitter/electronics/pcb/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (lib (name stm32f1-nrf24l01-transmitter-rescue)(type Legacy)(uri ${KIPRJMOD}/stm32f1-nrf24l01-transmitter-pcb-rescue.lib)(options "")(descr "")) 3 | ) 4 | -------------------------------------------------------------------------------- /transmitter/electronics/pcb/tact-switch.pretty/Tact switch 6x4x3.kicad_mod: -------------------------------------------------------------------------------- 1 | (module "Tact switch 6x4x3" (layer F.Cu) (tedit 5BA0EB05) 2 | (fp_text reference REF** (at 0 0) (layer F.SilkS) 3 | (effects (font (size 1 1) (thickness 0.15))) 4 | ) 5 | (fp_text value "Tact switch 6x4x3" (at 0 -3.81) (layer F.Fab) 6 | (effects (font (size 1 1) (thickness 0.15))) 7 | ) 8 | (fp_line (start -3 -2) (end -3 2) (layer F.SilkS) (width 0.15)) 9 | (fp_line (start -3 2) (end 3 2) (layer F.SilkS) (width 0.15)) 10 | (fp_line (start 3 2) (end 3 -2) (layer F.SilkS) (width 0.15)) 11 | (fp_line (start 3 -2) (end -3 -2) (layer F.SilkS) (width 0.15)) 12 | (pad 1 smd rect (at -3.9 0) (size 1.5 1.5) (layers F.Cu F.Paste F.Mask)) 13 | (pad 2 smd rect (at 3.9 0) (size 1.5 1.5) (layers F.Cu F.Paste F.Mask)) 14 | ) 15 | -------------------------------------------------------------------------------- /transmitter/electronics/prototyping-board-nrf-on-top/fp-info-cache: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /transmitter/electronics/prototyping-board-nrf-on-top/stm32f1-nrf24l01-transmitter-nrf_on_top.pro: -------------------------------------------------------------------------------- 1 | update=Thu 09 Feb 2017 08:38:20 AM SGT 2 | version=1 3 | last_client=pcbnew 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [cvpcb] 9 | version=1 10 | NetIExt=net 11 | [eeschema] 12 | version=1 13 | LibDir= 14 | [eeschema/libraries] 15 | LibName1=power 16 | LibName2=device 17 | LibName3=transistors 18 | LibName4=conn 19 | LibName5=linear 20 | LibName6=regul 21 | LibName7=74xx 22 | LibName8=cmos4000 23 | LibName9=adc-dac 24 | LibName10=memory 25 | LibName11=xilinx 26 | LibName12=microcontrollers 27 | LibName13=dsp 28 | LibName14=microchip 29 | LibName15=analog_switches 30 | LibName16=motorola 31 | LibName17=texas 32 | LibName18=intel 33 | LibName19=audio 34 | LibName20=interface 35 | LibName21=digital-audio 36 | LibName22=philips 37 | LibName23=display 38 | LibName24=cypress 39 | LibName25=siliconi 40 | LibName26=opto 41 | LibName27=atmel 42 | LibName28=contrib 43 | LibName29=valves 44 | [pcbnew] 45 | version=1 46 | PageLayoutDescrFile= 47 | LastNetListRead=stm32f1-nrf24l01-transmitter.net 48 | PadDrill=0.6 49 | PadDrillOvalY=0.6 50 | PadSizeH=1.5 51 | PadSizeV=1.5 52 | PcbTextSizeV=1.5 53 | PcbTextSizeH=1.5 54 | PcbTextThickness=0.3 55 | ModuleTextSizeV=1 56 | ModuleTextSizeH=1 57 | ModuleTextSizeThickness=0.15 58 | SolderMaskClearance=0 59 | SolderMaskMinWidth=0 60 | DrawSegmentWidth=0.2 61 | BoardOutlineThickness=0.09999999999999999 62 | ModuleOutlineThickness=0.15 63 | -------------------------------------------------------------------------------- /transmitter/electronics/prototyping-board-nrf-on-top/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (lib (name stm32f1-nrf24l01-transmitter-rescue)(type Legacy)(uri ${KIPRJMOD}/stm32f1-nrf24l01-transmitter-rescue.lib)(options "")(descr "")) 3 | ) 4 | -------------------------------------------------------------------------------- /transmitter/electronics/prototyping-board/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (lib (name WLA)(type KiCad)(uri /home/laneboysrc/projects/rc-headless-transmitter/transmitter/electronics/WLA.pretty)(options "")(descr "")) 3 | ) 4 | -------------------------------------------------------------------------------- /transmitter/electronics/prototyping-board/stm32f1-nrf24l01-transmitter.pro: -------------------------------------------------------------------------------- 1 | update=Fri Jan 17 14:12:10 2020 2 | version=1 3 | last_client=kicad 4 | [pcbnew] 5 | version=1 6 | LastNetListRead= 7 | UseCmpFile=1 8 | PadDrill=0.600000000000 9 | PadDrillOvalY=0.600000000000 10 | PadSizeH=1.500000000000 11 | PadSizeV=1.500000000000 12 | PcbTextSizeV=1.500000000000 13 | PcbTextSizeH=1.500000000000 14 | PcbTextThickness=0.300000000000 15 | ModuleTextSizeV=1.000000000000 16 | ModuleTextSizeH=1.000000000000 17 | ModuleTextSizeThickness=0.150000000000 18 | SolderMaskClearance=0.000000000000 19 | SolderMaskMinWidth=0.000000000000 20 | DrawSegmentWidth=0.200000000000 21 | BoardOutlineThickness=0.100000000000 22 | ModuleOutlineThickness=0.150000000000 23 | [pcbnew/libraries] 24 | LibName1=WLA-power 25 | LibName2=WLA-nrf24 26 | LibName3=sockets 27 | LibName4=connect 28 | LibName5=discret 29 | LibName6=pin_array 30 | LibName7=divers 31 | LibName8=smd_capacitors 32 | LibName9=smd_resistors 33 | LibName10=smd_crystal&oscillator 34 | LibName11=smd_dil 35 | LibName12=smd_transistors 36 | LibName13=libcms 37 | LibName14=display 38 | LibName15=led 39 | LibName16=dip_sockets 40 | LibName17=pga_sockets 41 | LibName18=valves 42 | LibDir=../../../../kicad-libraries 43 | [general] 44 | version=1 45 | [cvpcb] 46 | version=1 47 | NetIExt=net 48 | [cvpcb/libraries] 49 | EquName1=devcms 50 | [eeschema] 51 | version=1 52 | LibDir= 53 | -------------------------------------------------------------------------------- /transmitter/electronics/prototyping-board/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (lib (name stm32f1-nrf24l01-transmitter-rescue)(type Legacy)(uri ${KIPRJMOD}/stm32f1-nrf24l01-transmitter-rescue.lib)(options "")(descr "")) 3 | ) 4 | -------------------------------------------------------------------------------- /transmitter/electronics/stm32f1-nrf24l01-transmitter.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/electronics/stm32f1-nrf24l01-transmitter.pdf -------------------------------------------------------------------------------- /transmitter/electronics/world-engines/fp-info-cache: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /transmitter/electronics/world-engines/stm32f1-nrf24l01-transmitter-world-engines.pro: -------------------------------------------------------------------------------- 1 | update=22/05/2015 07:44:53 2 | version=1 3 | last_client=kicad 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [pcbnew] 9 | version=1 10 | LastNetListRead= 11 | UseCmpFile=1 12 | PadDrill=0.600000000000 13 | PadDrillOvalY=0.600000000000 14 | PadSizeH=1.500000000000 15 | PadSizeV=1.500000000000 16 | PcbTextSizeV=1.500000000000 17 | PcbTextSizeH=1.500000000000 18 | PcbTextThickness=0.300000000000 19 | ModuleTextSizeV=1.000000000000 20 | ModuleTextSizeH=1.000000000000 21 | ModuleTextSizeThickness=0.150000000000 22 | SolderMaskClearance=0.000000000000 23 | SolderMaskMinWidth=0.000000000000 24 | DrawSegmentWidth=0.200000000000 25 | BoardOutlineThickness=0.100000000000 26 | ModuleOutlineThickness=0.150000000000 27 | [cvpcb] 28 | version=1 29 | NetIExt=net 30 | [eeschema] 31 | version=1 32 | LibDir= 33 | [eeschema/libraries] 34 | -------------------------------------------------------------------------------- /transmitter/electronics/world-engines/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (lib (name stm32f1-nrf24l01-transmitter-rescue)(type Legacy)(uri ${KIPRJMOD}/stm32f1-nrf24l01-transmitter-rescue.lib)(options "")(descr "")) 3 | ) 4 | -------------------------------------------------------------------------------- /transmitter/firmware/.gitignore: -------------------------------------------------------------------------------- 1 | _build/* 2 | openocd.log 3 | st-link-serial.cfg 4 | -------------------------------------------------------------------------------- /transmitter/firmware/INO-FILE-COMPILATION.md: -------------------------------------------------------------------------------- 1 | (This is not relevant at the moment, but may become if we reuse the Open Source Multi-protocol transmitter module code) 2 | 3 | Hint for compiling .ino files: 4 | 5 | INOFLAGS += -x c 6 | INOFLAGS += -include stdint.h 7 | INOFLAGS += -include string.h 8 | INOFLAGS += -include libs/libopencm3/include/libopencm3/stm32/gpio.h 9 | INOFLAGS += -include multiprotocol/Pins.h 10 | INOFLAGS += -include multiprotocol/Multiprotocol.h 11 | 12 | C_OBJECTS = $(addprefix $(OBJECT_DIRECTORY)/, $(patsubst %.ino, %.o, $(C_SOURCE_FILE_NAMES:.c=.o) )) 13 | vpath %.ino $(C_PATHS) 14 | 15 | # Create objects from .ino source files 16 | $(OBJECT_DIRECTORY)/%.o : %.ino 17 | @echo [CC] $(notdir $<) 18 | $(ECHO)$(CC) $(INOFLAGS) -DNRF24L01_INSTALLED $(INC_PATHS) -c -o $@ $< 19 | -------------------------------------------------------------------------------- /transmitter/firmware/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the firmware that goes into the [*headless transmitter*](https://github.com/laneboysrc/rc-headless-transmitter). 2 | 3 | It runs on the STM32F103C8T6 micro-controller. 4 | 5 | The software makes used of [libopencm3](http://libopencm3.org/). 6 | 7 | The firmware is built using a [GNU Make](https://www.gnu.org/software/make/) makefile. The compiler is [ARM GCC toolchain](https://launchpad.net/gcc-arm-embedded), version 5.2.1. [OpenOCD](http://openocd.org/) is used to program the firmware into the micro-controller. 8 | 9 | In the [Makefile](Makefile), adjust the variable `GNU_INSTALL_ROOT` and `OPENOCD` to match your installation. 10 | 11 | Run 12 | 13 | make 14 | 15 | to build the firmware. When done, run 16 | 17 | make program 18 | 19 | to flash the firmware into the micro-controller. 20 | 21 | -------------------------------------------------------------------------------- /transmitter/firmware/configuration/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #define CONFIG_VERSION 1 11 | 12 | typedef enum { 13 | RF_PROTOCOL_HK310, 14 | RF_PROTOCOL_LANEBOYSRC4CH, 15 | RF_PROTOCOL_LANEBOYSRC8CH 16 | } rf_protocol_type_t; 17 | 18 | typedef struct { 19 | uint8_t uuid[8]; 20 | char name[16]; 21 | uint32_t last_changed; 22 | uint32_t tag; // For use by the configurator internally 23 | hardware_input_t hardware_inputs[MAX_TRANSMITTER_INPUTS]; 24 | logical_input_t logical_inputs[MAX_LOGICAL_INPUTS]; 25 | int32_t trim_range; 26 | int32_t trim_step_size; 27 | uint32_t bind_timeout_ms; 28 | uint32_t double_click_timeout_ms; 29 | uint16_t passphrase; 30 | uint8_t led_pwm_percent; 31 | uint8_t meter_pwm_percent; 32 | } tx_t; 33 | 34 | typedef struct { 35 | uint8_t uuid[8]; 36 | char name[16]; 37 | uint32_t last_changed; 38 | uint32_t tag; // For use by the configurator internally 39 | mixer_unit_t mixer_units[MAX_MIXER_UNITS]; 40 | limits_t limits[NUMBER_OF_RF_CHANNELS]; 41 | rf_protocol_type_t rf_protocol_type; 42 | union { 43 | protocol_hk310_t protocol_hk310; 44 | } rf; 45 | } model_t; 46 | 47 | typedef struct { 48 | uint32_t version; 49 | tx_t tx; 50 | model_t model; 51 | } config_t; 52 | 53 | 54 | extern config_t config; 55 | extern const config_t config_flash; 56 | 57 | void CONFIG_init(void); 58 | void CONFIG_load(void); 59 | void CONFIG_save(void); 60 | void CONFIG_background_flash_write(void); 61 | void CONFIG_dump_javascript_information(void); 62 | 63 | -------------------------------------------------------------------------------- /transmitter/firmware/gdb.flash.script: -------------------------------------------------------------------------------- 1 | target extended-remote /dev/ttyACM0 2 | monitor swdp_scan 3 | attach 1 4 | file _build/tx.out 5 | load 6 | kill 7 | quit 8 | -------------------------------------------------------------------------------- /transmitter/firmware/input/inputs_stm32f103c8t6.c: -------------------------------------------------------------------------------- 1 | // Inputs available on the AliExpress STM32F103C8T6 board 2 | 3 | #include 4 | 5 | #include 6 | 7 | 8 | const uint8_t adc_channel_selection[NUMBER_OF_ADC_CHANNELS] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 17}; 9 | 10 | 11 | const pcb_input_t pcb_inputs[MAX_TRANSMITTER_INPUTS] = { 12 | // Note: ADC0 is used for measuring the battery voltage, therefore it does 13 | // not appear in this list 14 | 15 | {.type = ANALOG_DIGITAL, .gpioport = GPIOA, .gpio = GPIO1, .adc_channel = 1, 16 | .pin_name = "PA1/ADC1"}, 17 | 18 | {.type = ANALOG_DIGITAL, .gpioport = GPIOA, .gpio = GPIO2, .adc_channel = 2, 19 | .pin_name = "PA2/ADC2"}, 20 | 21 | {.type = ANALOG_DIGITAL, .gpioport = GPIOA, .gpio = GPIO3, .adc_channel = 3, 22 | .pin_name = "PA3/ADC3"}, 23 | 24 | {.type = ANALOG_DIGITAL, .gpioport = GPIOA, .gpio = GPIO4, .adc_channel = 4, 25 | .pin_name = "PA4/ADC4"}, 26 | 27 | {.type = ANALOG_DIGITAL, .gpioport = GPIOA, .gpio = GPIO5, .adc_channel = 5, 28 | .pin_name = "PA5/ADC5"}, 29 | 30 | {.type = ANALOG_DIGITAL, .gpioport = GPIOA, .gpio = GPIO6, .adc_channel = 6, 31 | .pin_name = "PA6/ADC6"}, 32 | 33 | {.type = ANALOG_DIGITAL, .gpioport = GPIOA, .gpio = GPIO7, .adc_channel = 7, 34 | .pin_name = "PA7/ADC7"}, 35 | 36 | {.type = ANALOG_DIGITAL, .gpioport = GPIOB, .gpio = GPIO0, .adc_channel = 8, 37 | .pin_name = "PB0/ADC8"}, 38 | 39 | {.type = ANALOG_DIGITAL, .gpioport = GPIOB, .gpio = GPIO1, .adc_channel = 9, 40 | .pin_name = "PB1/ADC9"}, 41 | 42 | {.type = DIGITAL, .gpioport = GPIOB, .gpio = GPIO3, 43 | .pin_name = "PB3"}, 44 | 45 | {.type = DIGITAL, .gpioport = GPIOB, .gpio = GPIO4, 46 | .pin_name = "PB4"}, 47 | 48 | {.type = DIGITAL, .gpioport = GPIOB, .gpio = GPIO5, 49 | .pin_name = "PB5"}, 50 | 51 | {.type = DIGITAL, .gpioport = GPIOB, .gpio = GPIO6, 52 | .pin_name = "PB6"}, 53 | 54 | {.type = DIGITAL, .gpioport = GPIOB, .gpio = GPIO7, 55 | .pin_name = "PB7"}, 56 | 57 | {.type = DIGITAL, .gpioport = GPIOB, .gpio = GPIO8, 58 | .pin_name = "PB8"}, 59 | 60 | // NOTE: PB9 cannot be used as we reserve it for Analog battery meter output 61 | // {.type = DIGITAL, .gpioport = GPIOB, .gpio = GPIO9, 62 | // .pin_name = "PB9"}, 63 | 64 | {.type = DIGITAL, .gpioport = GPIOB, .gpio = GPIO10, 65 | .pin_name = "PB10"}, 66 | 67 | {.type = DIGITAL, .gpioport = GPIOB, .gpio = GPIO11, 68 | .pin_name = "PB11"} 69 | }; 70 | 71 | -------------------------------------------------------------------------------- /transmitter/firmware/input/inputs_stm32f103c8t6.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // NUMBER_OF_ADC_CHANNELS defines how many channels we measure using the ADC. 4 | // It includes all analog inputs to the transmitter (sticks, pots), as well 5 | // as the battery voltage input and the (internal!) voltage reference. 6 | // 7 | // IMPORTANT: 8 | // This value must be equal to the number of elements in adc_channel_selection! 9 | #define NUMBER_OF_ADC_CHANNELS 11 10 | 11 | // Index of battery voltage ADC inputs in adc_array_*. 12 | // Corresponds to the order of the adc channels in adc_channel_selection[]. 13 | #define BATTERY_VOLTAGE_INDEX 9 14 | #define REFERENCE_VOLTAGE_INDEX 10 15 | 16 | // NUMBER_OF_DIGITAL_INPUTS defines the maximum number of digital inputs 17 | // the cardware can support. Note that analog inputs can be configured 18 | // as digital inputs as well, so they must be included in this number even 19 | // when used in their analog form. 20 | // The STM32F103C8T6 board we are using has 9 analog/digital inputs and 8 21 | // digital inputs, so 17 in total. 22 | #define NUMBER_OF_DIGITAL_INPUTS 17 23 | 24 | 25 | extern const pcb_input_t pcb_inputs[]; 26 | extern const uint8_t adc_channel_selection[NUMBER_OF_ADC_CHANNELS]; -------------------------------------------------------------------------------- /transmitter/firmware/mixer/channels.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int32_t channels[NUMBER_OF_CHANNELS]; 6 | int32_t rf_channels[NUMBER_OF_RF_CHANNELS]; 7 | int32_t failsafe[NUMBER_OF_RF_CHANNELS]; 8 | -------------------------------------------------------------------------------- /transmitter/firmware/mixer/channels.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // A "channel" has a value range from -10000..0..10000, corresponding to 6 | // -100%..0..100%. This range is the same used in Deviation. It provides 7 | // good resolution and it is human readable. 8 | // 9 | // This range applies to the normalized input channels as well as to the 10 | // output channels. 11 | // 12 | // The normalized input channels are clamped to -10000..0..10000, while 13 | // the output channels can go up to -18000..0..18000 (-180%..0%..180%), 14 | // corresponding to receiver pulses of 600us..1500us..2400us 15 | #define CHANNEL_100_PERCENT 10000 16 | #define CHANNEL_CENTER 0 17 | #define CHANNEL_N100_PERCENT -10000 18 | 19 | #define CHANNEL_TO_PERCENT(x) ((x) / 100) 20 | #define PERCENT_TO_CHANNEL(x) ((x) * 100) 21 | 22 | // Channels sent to the receiver 23 | #define NUMBER_OF_RF_CHANNELS 8 24 | // Virtual Channels allow users to build complex mixer chains 25 | #define NUMBER_OF_VIRTUAL_CHANNELS 10 26 | // Hidden Virtual Channels enable the UI to build complex mixer chains that 27 | // are hidden from the user (for high-level mixers such as Elevons, 4-wheel 28 | // steering ...) 29 | #define NUMBER_OF_HIDDEN_VIRTUAL_CHANNELS 50 30 | #define NUMBER_OF_CHANNELS (NUMBER_OF_RF_CHANNELS + NUMBER_OF_VIRTUAL_CHANNELS + NUMBER_OF_HIDDEN_VIRTUAL_CHANNELS) 31 | 32 | 33 | // Tags to access the output channels 34 | // IMPORTANT: 35 | // ========== 36 | // If you modify this list, update src_label_t in mixer.h to match! 37 | typedef enum { 38 | // The following items must be in sequence: 39 | 40 | // NUMBER_OF_RF_CHANNELS channels (CH1..CHxxx), 41 | CH1 = 0, 42 | CH2, 43 | CH3, 44 | CH4, 45 | CH5, 46 | CH6, 47 | CH7, 48 | CH8, 49 | 50 | // NUMBER_OF_VIRTUAL_CHANNELS virtual channels (VIRTUAL1..VIRTUALxxx), 51 | VIRTUAL1, 52 | VIRTUAL2, 53 | VIRTUAL3, 54 | VIRTUAL4, 55 | VIRTUAL5, 56 | VIRTUAL6, 57 | VIRTUAL7, 58 | VIRTUAL8, 59 | VIRTUAL9, 60 | VIRTUAL10, 61 | 62 | // NUMBER_OF_HIDDEN_VIRTUAL_CHANNELS hidden channels to be used by the 63 | // complex mixer UI (HIDDEN1..HIDDENxxx) 64 | HIDDEN1, 65 | HIDDEN2, 66 | HIDDEN3, 67 | HIDDEN4, 68 | HIDDEN5, 69 | HIDDEN6, 70 | HIDDEN7, 71 | HIDDEN8, 72 | HIDDEN9, 73 | HIDDEN10, 74 | HIDDEN11, 75 | HIDDEN12, 76 | HIDDEN13, 77 | HIDDEN14, 78 | HIDDEN15, 79 | HIDDEN16, 80 | HIDDEN17, 81 | HIDDEN18, 82 | HIDDEN19, 83 | HIDDEN20, 84 | HIDDEN21, 85 | HIDDEN22, 86 | HIDDEN23, 87 | HIDDEN24, 88 | HIDDEN25, 89 | HIDDEN26, 90 | HIDDEN27, 91 | HIDDEN28, 92 | HIDDEN29, 93 | HIDDEN30, 94 | HIDDEN31, 95 | HIDDEN32, 96 | HIDDEN33, 97 | HIDDEN34, 98 | HIDDEN35, 99 | HIDDEN36, 100 | HIDDEN37, 101 | HIDDEN38, 102 | HIDDEN39, 103 | HIDDEN40, 104 | HIDDEN41, 105 | HIDDEN42, 106 | HIDDEN43, 107 | HIDDEN44, 108 | HIDDEN45, 109 | HIDDEN46, 110 | HIDDEN47, 111 | HIDDEN48, 112 | HIDDEN49, 113 | HIDDEN50, 114 | } channel_label_t; 115 | 116 | 117 | // Channel output destinations for the mixer 118 | extern int32_t channels[NUMBER_OF_CHANNELS]; 119 | 120 | // Channel outputs and failsafe values passed to the radio module 121 | extern int32_t rf_channels[NUMBER_OF_RF_CHANNELS]; 122 | extern int32_t failsafe[NUMBER_OF_RF_CHANNELS]; 123 | -------------------------------------------------------------------------------- /transmitter/firmware/mixer/curves.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define CURVE_MAX_POINTS 13 6 | 7 | typedef enum { 8 | CURVE_NONE, 9 | CURVE_FIXED, 10 | CURVE_MIN_MAX, 11 | CURVE_ZERO_MAX, 12 | CURVE_GT_ZERO, 13 | CURVE_LT_ZERO, 14 | CURVE_ABSVAL, 15 | CURVE_EXPO, 16 | CURVE_DEADBAND, 17 | CURVE_3POINT, 18 | CURVE_5POINT, 19 | CURVE_7POINT, 20 | CURVE_9POINT, 21 | CURVE_11POINT, 22 | CURVE_13POINT 23 | } curve_type_t; 24 | 25 | typedef enum { 26 | INTERPOLATION_LINEAR, 27 | INTERPOLATION_SMOOTHING 28 | } interpolation_type_t; 29 | 30 | typedef struct { 31 | curve_type_t type; 32 | interpolation_type_t smoothing; 33 | int8_t points[CURVE_MAX_POINTS]; 34 | } curve_t; 35 | 36 | 37 | int32_t CURVE_evaluate(curve_t *curve, int32_t value); 38 | unsigned CURVE_get_number_of_points(curve_t *curve); 39 | -------------------------------------------------------------------------------- /transmitter/firmware/mixer/limits.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define MIN(a,b) (((a)<(b))?(a):(b)) 10 | #define MAX(a,b) (((a)>(b))?(a):(b)) 11 | 12 | 13 | // **************************************************************************** 14 | void LIMITS_apply(void) 15 | { 16 | static uint32_t last_ms = 0; 17 | uint32_t elapsed_ms; 18 | 19 | elapsed_ms = milliseconds - last_ms; 20 | last_ms = milliseconds; 21 | 22 | for (int i = 0; i < NUMBER_OF_RF_CHANNELS; i++) { 23 | limits_t *l = &config.model.limits[i]; 24 | int32_t input_value = channels[i]; 25 | int32_t output_value; 26 | int32_t failsafe_value; 27 | 28 | // Map the channel CHANNEL_100_NPERCENT .. 0 .. CHANNEL_100_PERCENT to 29 | // (ep_l .. 0 .. ep_h) + subtrim. 30 | // This way the end points dictate throw (not hard stop) and the subtrim 31 | // dictates the center around the throws 32 | if (input_value >= 0) { 33 | output_value = l->subtrim + l->ep_h * input_value / CHANNEL_100_PERCENT; 34 | } 35 | else { 36 | output_value = l->subtrim + l->ep_l * input_value / CHANNEL_N100_PERCENT; 37 | } 38 | 39 | // Limit the speed a servo output can change. 40 | // l->speed unit is "degrees per 100 ms" 41 | if (l->speed) { 42 | int32_t rate; 43 | 44 | rate = CHANNEL_100_PERCENT * l->speed / 60 * elapsed_ms / 100; 45 | if (output_value - rf_channels[i] > rate) { 46 | output_value = rf_channels[i] + rate; 47 | } 48 | else if (output_value - rf_channels[i] < -rate) { 49 | output_value = rf_channels[i] - rate; 50 | } 51 | } 52 | 53 | // Clamp to the configurable limits 54 | output_value = MAX(output_value, l->limit_l); 55 | output_value = MIN(output_value, l->limit_h); 56 | 57 | // Set the failsafe value, applying subtrim 58 | failsafe_value = l->subtrim + l->failsafe; 59 | 60 | // Invert the channel if requested 61 | if (l->invert) { 62 | output_value = -output_value; 63 | failsafe_value = -failsafe_value; 64 | } 65 | 66 | // Clamp to hard-coded limits of +/-180% to ensure valid servo pulses 67 | output_value = MAX(output_value, HARD_LIMIT_L); 68 | output_value = MIN(output_value, HARD_LIMIT_H); 69 | failsafe_value = MAX(failsafe_value, HARD_LIMIT_L); 70 | failsafe_value = MIN(failsafe_value, HARD_LIMIT_H); 71 | 72 | rf_channels[i] = output_value; 73 | failsafe[i] = failsafe_value; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /transmitter/firmware/mixer/limits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define HARD_LIMIT_L -18000 6 | #define HARD_LIMIT_H 18000 7 | 8 | typedef struct { 9 | int32_t ep_l; 10 | int32_t ep_h; 11 | int32_t subtrim; 12 | int32_t limit_l; 13 | int32_t limit_h; 14 | int32_t failsafe; 15 | uint8_t speed; // limit servo speed to "degrees per 100 ms", 0 = no limit 16 | uint8_t invert; 17 | } limits_t; 18 | 19 | void LIMITS_apply(void); -------------------------------------------------------------------------------- /transmitter/firmware/rf/protocol_hk310.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define ADDRESS_SIZE 5 6 | #define NUMBER_OF_HOP_CHANNELS 20 7 | 8 | typedef struct { 9 | uint8_t hop_channels[NUMBER_OF_HOP_CHANNELS]; 10 | uint8_t address[ADDRESS_SIZE]; 11 | } protocol_hk310_t; 12 | 13 | void PROTOCOL_HK310_init(void); 14 | void PROTOCOL_HK310_enable_binding(void); 15 | void PROTOCOL_HK310_disable_binding(void); 16 | 17 | // Semi-private function to support the modded 4-channel protocol LANEBoysRC-4CH 18 | void PROTOCOL_HK310_init_ex(uint8_t number_of_channels); 19 | -------------------------------------------------------------------------------- /transmitter/firmware/rf/protocol_laneboysrc4ch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* **************************************************************************** 5 | 6 | RF protocol compatible with the LANE Boys RC nrf24le01-rc receiver. 7 | 8 | This protocol is mostly like the HK310 protocol, but with the two unused bytes 9 | utilized to transmit a 4th channel. 10 | 11 | To distinguish between the HK310 and the LANEBoysRC-4CH protocol the following 12 | data has been changed: 13 | - Stick data uses packet id 0x56 (instead of 0x55) 14 | - Failsafe data uses packet id 0xab (instead of 0xaa) 15 | - The first bind packet uses 0xff 0xab 0x56 as special marker (instead of 0xff 0xaa 0x55) 16 | 17 | Since the protocol is almost identical to the HK310 its actual implementation 18 | has been merged into "protocol_hk310.c". 19 | 20 | */ 21 | 22 | // **************************************************************************** 23 | void PROTOCOL_LANEBOYSRC4CH_init(void) 24 | { 25 | PROTOCOL_HK310_init_ex(4); 26 | } 27 | 28 | // **************************************************************************** 29 | void PROTOCOL_LANEBOYSRC4CH_enable_binding(void) 30 | { 31 | PROTOCOL_HK310_enable_binding(); 32 | } 33 | 34 | // **************************************************************************** 35 | void PROTOCOL_LANEBOYSRC4CH_disable_binding(void) 36 | { 37 | PROTOCOL_HK310_disable_binding(); 38 | } 39 | -------------------------------------------------------------------------------- /transmitter/firmware/rf/protocol_laneboysrc4ch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void PROTOCOL_LANEBOYSRC4CH_init(void); 4 | void PROTOCOL_LANEBOYSRC4CH_enable_binding(void); 5 | void PROTOCOL_LANEBOYSRC4CH_disable_binding(void); 6 | -------------------------------------------------------------------------------- /transmitter/firmware/rf/protocol_laneboysrc8ch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void PROTOCOL_LANEBOYSRC8CH_init(void); 4 | void PROTOCOL_LANEBOYSRC8CH_enable_binding(void); 5 | void PROTOCOL_LANEBOYSRC8CH_disable_binding(void); 6 | -------------------------------------------------------------------------------- /transmitter/firmware/sound/music.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | uint16_t frequency; 7 | uint16_t duration_ms; 8 | } tone; 9 | 10 | typedef struct { 11 | uint8_t volume; 12 | const tone *tones; 13 | } song; 14 | 15 | extern const song song_startup; 16 | extern const song song_activate; 17 | extern const song song_deactivate; 18 | extern const song song_shutdown; 19 | extern const song song_alarm1; 20 | extern const song song_alarm_battery_low; 21 | extern const song song_alarm_battery_very_low; 22 | extern const song song_config_invalid; 23 | extern const song song_connecting; 24 | extern const song song_disconnecting; 25 | 26 | void MUSIC_play(song const *s); 27 | -------------------------------------------------------------------------------- /transmitter/firmware/sound/sound.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | // **************************************************************************** 7 | // Frequencies for each note in Hz 8 | // Source: http://www.phy.mtu.edu/~suits/notefreqs.html 9 | #define C3 131 10 | #define D3 147 11 | #define E3 165 12 | #define F3 175 13 | #define G3 196 14 | #define A3 220 15 | #define B3 247 16 | #define C4 262 17 | #define D4 294 18 | #define E4 330 19 | #define F4 349 20 | #define G4 392 21 | #define A4 440 22 | #define B4 494 23 | #define C5 523 24 | #define D5 587 25 | #define E5 659 26 | #define F5 698 27 | #define G5 784 28 | #define A5 880 29 | #define B5 988 30 | #define C6 1047 31 | #define D6 1175 32 | #define E6 1319 33 | #define F6 1397 34 | #define G6 1568 35 | #define A6 1760 36 | #define B6 1976 37 | #define C7 2093 38 | #define D7 2349 39 | #define E7 2637 40 | #define F7 2794 41 | #define G7 3136 42 | #define A7 3520 43 | #define B7 3951 44 | #define C8 4186 45 | #define D8 4699 46 | #define E8 5274 47 | #define F8 5588 48 | #define G8 6272 49 | #define A8 7040 50 | #define B8 7902 51 | 52 | #define PAUSE 0 53 | 54 | 55 | void SOUND_init(void); 56 | void SOUND_set_volume(uint8_t volume); 57 | void SOUND_play(unsigned int frequency, uint32_t duration_ms, void(* cb)(void)); 58 | void SOUND_stop(void); 59 | -------------------------------------------------------------------------------- /transmitter/firmware/stm32f103c8t6.ld: -------------------------------------------------------------------------------- 1 | /* Linker script for AliExpress STM32F103 board (STM32F103C8T6, 64K flash, 20K RAM). */ 2 | 3 | MEMORY 4 | { 5 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 64K 6 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 7 | } 8 | 9 | 10 | /* Put the transmitter configuration in the upper-most 8 KBytes of flash */ 11 | SECTIONS 12 | { 13 | .persistent_settings 0x0800e000 : 14 | { 15 | *(.rodata.logical_inputs_flash) 16 | } 17 | .transmitter_configuration 0x0800e400 : 18 | { 19 | *(.rodata.config_flash) 20 | } 21 | } 22 | 23 | 24 | INCLUDE libopencm3_stm32f1.ld 25 | 26 | 27 | -------------------------------------------------------------------------------- /transmitter/firmware/system/battery.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | typedef enum { 10 | BATTERY_OK, 11 | BATTERY_LOW, 12 | BATTERY_VERY_LOW, 13 | BATTERY_DANGEROUSLY_LOW 14 | } battery_state_t; 15 | 16 | static battery_state_t battery_state = BATTERY_OK; 17 | 18 | 19 | // **************************************************************************** 20 | static void battery_alarm_callback(void) 21 | { 22 | // Repeat the alarm in 1 minute, unless when dangerously low, then we 23 | // repeat it every 10 seconds 24 | SYSTICK_set_callback(battery_alarm_callback, 25 | (battery_state != BATTERY_DANGEROUSLY_LOW) ? 60 * 1000 : 10 * 1000); 26 | 27 | MUSIC_play((battery_state == BATTERY_LOW) ? 28 | &song_alarm_battery_low : &song_alarm_battery_very_low); 29 | } 30 | 31 | 32 | // **************************************************************************** 33 | void BATTERY_check_level(void) 34 | { 35 | uint32_t battery_voltage; 36 | 37 | battery_voltage = INPUTS_get_battery_voltage(); 38 | 39 | METER_show_level(battery_voltage); 40 | 41 | switch (battery_state) { 42 | case BATTERY_OK: 43 | if (battery_voltage < BATTERY_LOW_LEVEL) { 44 | battery_state = BATTERY_LOW; 45 | LED_flashing(); 46 | SYSTICK_set_callback(battery_alarm_callback, 1); 47 | } 48 | break; 49 | 50 | case BATTERY_LOW: 51 | if (battery_voltage < BATTERY_VERY_LOW_LEVEL) { 52 | battery_state = BATTERY_VERY_LOW; 53 | } 54 | break; 55 | 56 | case BATTERY_VERY_LOW: 57 | if (battery_voltage < BATTERY_DANGEROUSLY_LOW_LEVEL) { 58 | battery_state = BATTERY_DANGEROUSLY_LOW; 59 | } 60 | break; 61 | 62 | case BATTERY_DANGEROUSLY_LOW: 63 | default: 64 | break; 65 | } 66 | } -------------------------------------------------------------------------------- /transmitter/firmware/system/battery.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define BATTERY_FULL_LEVEL 4200 4 | #define BATTERY_LOW_LEVEL 3500 5 | #define BATTERY_VERY_LOW_LEVEL 3430 6 | #define BATTERY_DANGEROUSLY_LOW_LEVEL 3340 7 | 8 | void BATTERY_check_level(void); 9 | -------------------------------------------------------------------------------- /transmitter/firmware/system/led.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | static bool led_on; 12 | static bool led_flashing; 13 | 14 | static uint32_t flash_period_start_ms; 15 | 16 | 17 | #define LED_PWM_MODULO 10 18 | #define LED_ON_VALUE 3 19 | #define FLASH_PERIOD_MS 200 20 | 21 | 22 | // **************************************************************************** 23 | void LED_on(void) 24 | { 25 | led_on = true; 26 | led_flashing = false; 27 | } 28 | 29 | 30 | // **************************************************************************** 31 | void LED_off(void) 32 | { 33 | led_on = false; 34 | led_flashing = false; 35 | } 36 | 37 | 38 | // **************************************************************************** 39 | void LED_flashing(void) 40 | { 41 | if (led_flashing) { 42 | return; 43 | } 44 | 45 | led_on = true; 46 | led_flashing = true; 47 | flash_period_start_ms = milliseconds; 48 | } 49 | 50 | 51 | // **************************************************************************** 52 | void LED_systick_callback(void) 53 | { 54 | uint8_t led_brightness; 55 | 56 | if (led_flashing) { 57 | if ((milliseconds - flash_period_start_ms) >= FLASH_PERIOD_MS) { 58 | flash_period_start_ms = milliseconds; 59 | led_on = !led_on; 60 | } 61 | } 62 | 63 | led_brightness = config.tx.led_pwm_percent / LED_PWM_MODULO; 64 | if (led_on && (milliseconds % LED_PWM_MODULO) < led_brightness) { 65 | gpio_clear(GPIOC, GPIO13); 66 | } 67 | else { 68 | gpio_set(GPIOC, GPIO13); 69 | } 70 | } 71 | 72 | 73 | // **************************************************************************** 74 | void LED_init(void) 75 | { 76 | // Configure LED output port 77 | gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); 78 | gpio_set(GPIOC, GPIO13); 79 | 80 | LED_off(); 81 | } -------------------------------------------------------------------------------- /transmitter/firmware/system/led.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void LED_systick_callback(void); 4 | void LED_init(void); 5 | void LED_on(void); 6 | void LED_off(void); 7 | void LED_flashing(void); 8 | -------------------------------------------------------------------------------- /transmitter/firmware/system/meter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void METER_init(void); 6 | void METER_show_level(uint32_t battery_voltage_mv); 7 | -------------------------------------------------------------------------------- /transmitter/firmware/system/persistent_storage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void PERSISTENT_STORAGE_init(void); 4 | void PERSISTENT_STORAGE_background_flash_write(void); 5 | void PERSISTENT_STORAGE_save_config(void); 6 | void PERSISTENT_STORAGE_save_hardware_input_values(void); 7 | 8 | -------------------------------------------------------------------------------- /transmitter/firmware/system/serial_number.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | // Build a serial number string from the chip's unique ID. 6 | // The input buffer must have a size of at least 9 char. 7 | void SERIAL_NUMBER_get(char *buffer) 8 | { 9 | volatile uint32_t *unique_id_p = (volatile uint32_t *)0x1FFFF7E8; 10 | 11 | uint8_t i; 12 | uint32_t unique_id; 13 | 14 | // Combine all unique ID fields into a single 32-bit number 15 | unique_id = *unique_id_p + *(unique_id_p + 1) + *(unique_id_p + 2); 16 | 17 | // Split the unique ID into 8 nibbles 18 | for (i = 0; i < 8; i++) { 19 | buffer[i] = unique_id & 0x0f; 20 | unique_id = unique_id >> 4; 21 | } 22 | 23 | // Convert each nibble into a HEX digit 24 | for (i = 0; i < 8; i++) { 25 | if (buffer[i] > 9) { 26 | buffer[i] += 'A' - 10; 27 | } 28 | else { 29 | buffer[i] += '0'; 30 | } 31 | } 32 | 33 | // Terminate the buffer to form a C-string 34 | buffer[8] = 0; 35 | } 36 | -------------------------------------------------------------------------------- /transmitter/firmware/system/serial_number.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void SERIAL_NUMBER_get(char *serial_number_buffer); 4 | -------------------------------------------------------------------------------- /transmitter/firmware/system/spi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | 11 | // **************************************************************************** 12 | void SPI_init(void) 13 | { 14 | rcc_periph_clock_enable(RCC_SPI2); 15 | 16 | // Configure GPIOs: SS=PB12, SCK=PB13, MISO=PB14 and MOSI=PB15 17 | gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO13 | GPIO15); 18 | gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); 19 | gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO14); 20 | 21 | // Reset SPI, SPI_CR1 register cleared, SPI is disabled 22 | spi_reset(SPI2); 23 | 24 | // Set up SPI2 in Master mode 25 | spi_init_master(SPI2, SPI_CR1_BAUDRATE_FPCLK_DIV_16, 26 | SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_1, 27 | SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); 28 | 29 | // Configure chip select (SS) to be controlled by software 30 | spi_enable_software_slave_management(SPI2); 31 | spi_disable_ss_output(SPI2); 32 | spi_set_nss_high(SPI2); 33 | 34 | spi_enable(SPI2); 35 | } 36 | 37 | 38 | // **************************************************************************** 39 | uint8_t SPI_transaction(unsigned int count, uint8_t *buffer) 40 | { 41 | gpio_clear(GPIOB, GPIO12); 42 | 43 | for (unsigned int i = 0; i < count; i++) { 44 | uint8_t rx = (uint8_t)spi_xfer(SPI2, buffer[i]); 45 | buffer[i] = rx; 46 | } 47 | 48 | gpio_set(GPIOB, GPIO12); 49 | return *buffer; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /transmitter/firmware/system/spi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void SPI_init(void); 6 | uint8_t SPI_transaction(unsigned int count, uint8_t *buffer); 7 | 8 | -------------------------------------------------------------------------------- /transmitter/firmware/system/systick.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | typedef void (* systick_callback)(void); 7 | 8 | extern volatile uint32_t milliseconds; 9 | 10 | void SYSTICK_init(void); 11 | void SYSTICK_set_callback(systick_callback cb, uint32_t duration_ms); 12 | void SYSTICK_clear_callback(systick_callback cb); 13 | void SYSTICK_set_rf_callback(systick_callback cb, uint32_t repetition_time_ms); 14 | -------------------------------------------------------------------------------- /transmitter/firmware/system/uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | // **************************************************************************** 15 | #define BUFFER_SIZE 1024 16 | 17 | 18 | static uint8_t tx_buffer[BUFFER_SIZE]; 19 | static RING_BUFFER_T tx_ring_buffer; 20 | 21 | 22 | int _write(int file, char *ptr, int len); 23 | 24 | 25 | // **************************************************************************** 26 | void UART_init(void) 27 | { 28 | rcc_periph_clock_enable(RCC_USART1); 29 | 30 | RING_BUFFER_init(&tx_ring_buffer, tx_buffer, BUFFER_SIZE); 31 | 32 | // Setup GPIO pins GPIO_USART1_RE_TX on PA9 and GPIO_USART1_RE_RX on PA10 33 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, 34 | GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); 35 | 36 | gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 37 | GPIO_CNF_INPUT_FLOAT, GPIO_USART1_RX); 38 | 39 | usart_set_baudrate(USART1, 115200); 40 | usart_set_databits(USART1, 8); 41 | usart_set_stopbits(USART1, USART_STOPBITS_1); 42 | usart_set_parity(USART1, USART_PARITY_NONE); 43 | usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); 44 | usart_set_mode(USART1, USART_MODE_TX_RX); 45 | 46 | // Enable USART1 receive interrupt 47 | USART_CR1(USART1) |= USART_CR1_RXNEIE; 48 | 49 | nvic_enable_irq(NVIC_USART1_IRQ); 50 | usart_enable(USART1); 51 | } 52 | 53 | 54 | // **************************************************************************** 55 | void usart1_isr(void) 56 | { 57 | // Check if we were called because of RXNE 58 | if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) && 59 | ((USART_SR(USART1) & USART_SR_RXNE) != 0)) { 60 | 61 | uint16_t ch = usart_recv(USART1); 62 | 63 | (void) ch; 64 | } 65 | 66 | // Check if we were called because of TXE 67 | if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0) && 68 | ((USART_SR(USART1) & USART_SR_TXE) != 0)) { 69 | 70 | uint8_t data; 71 | 72 | // If there is still data in the transmit buffer send the next byte, 73 | // otherwise disable the TXE interrupt as it is no longer needed. 74 | if (RING_BUFFER_read_uint8(&tx_ring_buffer, &data)) { 75 | usart_send(USART1, data); 76 | } 77 | else { 78 | USART_CR1(USART1) &= ~USART_CR1_TXEIE; 79 | } 80 | } 81 | } 82 | 83 | 84 | // **************************************************************************** 85 | // Wait until the UART transmit buffer is empty 86 | void UART_sync(void) 87 | { 88 | while (! RING_BUFFER_is_empty(&tx_ring_buffer)) { 89 | // Put the CPU to sleep until an interrupt triggers. 90 | __WFI(); 91 | 92 | WATCHDOG_reset(); 93 | } 94 | } 95 | 96 | 97 | // **************************************************************************** 98 | int _write(int file, char *ptr, int len) 99 | { 100 | RING_BUFFER_SIZE_T written; 101 | 102 | if (file == 1) { 103 | written = RING_BUFFER_write(&tx_ring_buffer, (uint8_t *)ptr, len); 104 | 105 | // Enable the TXE interrupt 106 | USART_CR1(USART1) |= USART_CR1_TXEIE; 107 | 108 | return written; 109 | } 110 | 111 | errno = EIO; 112 | return -1; 113 | } -------------------------------------------------------------------------------- /transmitter/firmware/system/uart.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void UART_init(void); 4 | void UART_sync(void); 5 | -------------------------------------------------------------------------------- /transmitter/firmware/system/watchdog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | 7 | // **************************************************************************** 8 | void WATCHDOG_start(void) 9 | { 10 | iwdg_set_period_ms(100); 11 | iwdg_start(); 12 | } 13 | 14 | 15 | // **************************************************************************** 16 | void WATCHDOG_reset(void) 17 | { 18 | iwdg_reset(); 19 | } 20 | -------------------------------------------------------------------------------- /transmitter/firmware/system/watchdog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void WATCHDOG_start(void); 4 | void WATCHDOG_reset(void); 5 | -------------------------------------------------------------------------------- /transmitter/firmware/usb/webusb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void WEBUSB_poll(void); 4 | void WEBUSB_init(void); 5 | -------------------------------------------------------------------------------- /transmitter/firmware/utils/ring_buffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | // Ring buffer implementation from open-bldc's libgovernor 8 | // https://github.com/open-bldc/open-bldc/tree/master/source/libgovernor 9 | // 10 | // Heavily modified: all functions return the number of bytes read/written 11 | 12 | 13 | // **************************************************************************** 14 | void RING_BUFFER_init(RING_BUFFER_T *ring, uint8_t *buf, RING_BUFFER_SIZE_T size) 15 | { 16 | ring->data = buf; 17 | ring->size = size; 18 | ring->begin = 0; 19 | ring->end = 0; 20 | } 21 | 22 | 23 | // **************************************************************************** 24 | RING_BUFFER_SIZE_T RING_BUFFER_write_uint8(RING_BUFFER_T *ring, uint8_t value) 25 | { 26 | if (((ring->end + 1) % ring->size) != ring->begin) { 27 | ring->data[ring->end] = value; 28 | ring->end = (ring->end + 1) % ring->size; 29 | return 1; 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | 36 | // **************************************************************************** 37 | RING_BUFFER_SIZE_T RING_BUFFER_write(RING_BUFFER_T *ring, uint8_t *data, RING_BUFFER_SIZE_T size) 38 | { 39 | RING_BUFFER_SIZE_T i; 40 | 41 | for (i = 0; i < size; i++) { 42 | if (RING_BUFFER_write_uint8(ring, *data) == 0) { 43 | break; 44 | } 45 | ++data; 46 | } 47 | 48 | return i; 49 | } 50 | 51 | 52 | // **************************************************************************** 53 | RING_BUFFER_SIZE_T RING_BUFFER_read_uint8(RING_BUFFER_T *ring, uint8_t *data) 54 | { 55 | if (data != NULL) { 56 | if (ring->begin != ring->end) { 57 | *data = ring->data[ring->begin]; 58 | ring->begin = (ring->begin + 1) % ring->size; 59 | return 1; 60 | } 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | 67 | // **************************************************************************** 68 | RING_BUFFER_SIZE_T RING_BUFFER_read(RING_BUFFER_T *ring, uint8_t *data, RING_BUFFER_SIZE_T size) 69 | { 70 | RING_BUFFER_SIZE_T i; 71 | 72 | for (i = 0; i < size; i++) { 73 | if (RING_BUFFER_read_uint8(ring, data) == 0) { 74 | break; 75 | } 76 | ++data; 77 | } 78 | 79 | return i; 80 | } 81 | 82 | 83 | // **************************************************************************** 84 | bool RING_BUFFER_is_empty(RING_BUFFER_T *ring) 85 | { 86 | return (ring->begin == ring->end); 87 | } 88 | -------------------------------------------------------------------------------- /transmitter/firmware/utils/ring_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef uint16_t RING_BUFFER_SIZE_T; 7 | 8 | typedef struct { 9 | uint8_t *data; 10 | RING_BUFFER_SIZE_T size; 11 | uint32_t begin; 12 | uint32_t end; 13 | } RING_BUFFER_T; 14 | 15 | void RING_BUFFER_init(RING_BUFFER_T *ring, uint8_t *buf, RING_BUFFER_SIZE_T size); 16 | RING_BUFFER_SIZE_T RING_BUFFER_write(RING_BUFFER_T *ring, uint8_t *data, RING_BUFFER_SIZE_T size); 17 | RING_BUFFER_SIZE_T RING_BUFFER_write_uint8(RING_BUFFER_T *ring, uint8_t data); 18 | RING_BUFFER_SIZE_T RING_BUFFER_read(RING_BUFFER_T *ring, uint8_t *data, RING_BUFFER_SIZE_T size); 19 | RING_BUFFER_SIZE_T RING_BUFFER_read_uint8(RING_BUFFER_T *ring, uint8_t *data); 20 | bool RING_BUFFER_is_empty(RING_BUFFER_T *ring); 21 | -------------------------------------------------------------------------------- /transmitter/firmware/utils/slip.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // SLIP protocol implementation according to RFC1055 7 | // https://tools.ietf.org/html/rfc1055 8 | 9 | // Special SLIP character codes 10 | #define END 0xc0 // Start/end of packet 11 | #define ESC 0xdb // Byte stuffing 12 | #define ESC_END 0xdc // ESC ESC_END means END data byte 13 | #define ESC_ESC 0xdd // ESC ESC_ESC means ESC data byte 14 | 15 | 16 | // **************************************************************************** 17 | void SLIP_init(slip_t *s) 18 | { 19 | s->state = SLIP_IDLE; 20 | s->message_size = 0; 21 | } 22 | 23 | 24 | // **************************************************************************** 25 | bool SLIP_decode(slip_t *s, uint8_t new_input) 26 | { 27 | // If we are getting called after we received already a complete message, 28 | // re-initialize for receiving a new message 29 | if (s->state == SLIP_MESSAGE_RECEIVED) { 30 | SLIP_init(s); 31 | } 32 | 33 | // If the SLIP message is too long wait until it finishes, then start 34 | // capturing the next message. This means long messages are simply ignored. 35 | if (s->state == SLIP_OVERFLOW) { 36 | if (new_input == END) { 37 | SLIP_init(s); 38 | } 39 | return false; 40 | } 41 | 42 | switch (new_input) { 43 | case END: 44 | // We return True only if we received a message 45 | if (s->message_size) { 46 | s->state = SLIP_MESSAGE_RECEIVED; 47 | return true; 48 | } 49 | return false; 50 | 51 | case ESC: 52 | s->state = SLIP_ESC; 53 | break; 54 | 55 | default: 56 | if (s->state == SLIP_ESC) { 57 | s->state = SLIP_IDLE; 58 | switch (new_input) { 59 | case ESC_ESC: 60 | new_input = ESC; 61 | break; 62 | 63 | case ESC_END: 64 | new_input = END; 65 | break; 66 | 67 | // Protocol violation; handle it gracefully by ignoring ESC 68 | default: 69 | break; 70 | } 71 | } 72 | 73 | if (s->message_size < s->buffer_size) { 74 | s->buffer[s->message_size] = new_input; 75 | ++s->message_size; 76 | } 77 | else { 78 | s->state = SLIP_OVERFLOW; 79 | } 80 | break; 81 | } 82 | 83 | return false; 84 | } 85 | 86 | 87 | // **************************************************************************** 88 | void SLIP_encode(const uint8_t *data, uint8_t length, void (* callback)(uint8_t)) 89 | { 90 | if (callback == NULL || length == 0) { 91 | return; 92 | } 93 | 94 | callback(END); 95 | 96 | while (length) { 97 | 98 | switch (*data) { 99 | case END: 100 | callback(ESC); 101 | callback(ESC_END); 102 | break; 103 | 104 | case ESC: 105 | callback(ESC); 106 | callback(ESC_ESC); 107 | break; 108 | 109 | default: 110 | callback(*data); 111 | } 112 | 113 | --length; 114 | ++data; 115 | } 116 | 117 | callback(END); 118 | } -------------------------------------------------------------------------------- /transmitter/firmware/utils/slip.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | typedef enum { 8 | SLIP_IDLE = 0, 9 | SLIP_ESC, 10 | SLIP_OVERFLOW, 11 | SLIP_MESSAGE_RECEIVED 12 | } slip_state_t; 13 | 14 | typedef struct { 15 | uint8_t *buffer; 16 | uint8_t buffer_size; 17 | uint8_t message_size; 18 | slip_state_t state; 19 | } slip_t; 20 | 21 | 22 | void SLIP_init(slip_t *s); 23 | bool SLIP_decode(slip_t *s, uint8_t new_input); 24 | void SLIP_encode(const uint8_t *data, uint8_t length, void (* callback)(uint8_t)); 25 | -------------------------------------------------------------------------------- /transmitter/mechanics/devo4-antenna-guide.scad: -------------------------------------------------------------------------------- 1 | eps = 0.05; 2 | eps2 = 2 * eps; 3 | epsz = [0, 0, -eps]; 4 | 5 | $fn = 50; 6 | 7 | d_ring = 16; 8 | d_outer = 20; 9 | h = 9; 10 | h_rim = 2; 11 | z_rim = 2; 12 | 13 | d_screw = 6; 14 | screw_y = 6; 15 | 16 | d_antenna = 10; 17 | d_antenna_recess = 10; 18 | h_antenna = 3; 19 | 20 | 21 | difference() { 22 | union() { 23 | cylinder(d=d_ring, h=h); 24 | translate([0, 0, z_rim]) cylinder(d1=d_ring, d2=d_outer, h=1.5); 25 | translate([0, 0, z_rim+1.5]) cylinder(d=d_outer, h=h_rim-1.5); 26 | } 27 | 28 | translate(epsz) cylinder(d=d_antenna, h=h+eps2); 29 | translate(epsz) cylinder(d=d_antenna_recess, h=h-h_antenna+eps); 30 | 31 | translate([0, 0, screw_y-(d_screw/2)]) rotate([90, 0, 0]) cylinder(d=d_screw, h=d_outer+eps2, center=true); 32 | cube([d_screw, d_outer+eps, d_screw+(screw_y-(d_screw/2))/2], center=true); 33 | 34 | translate([d_ring/2, -d_outer/2, -eps]) cube([d_outer, d_outer, h+eps2]); 35 | mirror([1, 0, 0] )translate([d_ring/2, -d_outer/2, -eps]) cube([d_outer, d_outer, h+eps2]); 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /transmitter/mechanics/devo4-switch-mount.scad: -------------------------------------------------------------------------------- 1 | eps = 0.05; 2 | eps2 = 2 * eps; 3 | epsz = [0, 0, -eps]; 4 | $fa = 1; 5 | $fs = 0.5; 6 | 7 | x = 24.6; 8 | y = 10; 9 | h = 3; 10 | sw_x = 6; 11 | sw_w = 9; 12 | sw_y = 3.5; 13 | screw_distance = 19; 14 | screw_d = 1.6; 15 | screw_y = 2.5; 16 | 17 | screw_x = (x - screw_distance) / 2; 18 | 19 | 20 | difference() { 21 | cube([x, y, h]); 22 | translate([screw_x, screw_y, -eps]) cylinder(d=screw_d, h=h+eps2); 23 | translate([screw_x+screw_distance, screw_y, -eps]) cylinder(d=screw_d, h=h+eps2); 24 | 25 | translate([sw_x, sw_y, -eps])cube([sw_w, y, h+eps2]); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /transmitter/mechanics/esp8266-nrf51-configurator-case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/mechanics/esp8266-nrf51-configurator-case.png -------------------------------------------------------------------------------- /transmitter/mechanics/graupner-e8-antenna-guide.scad: -------------------------------------------------------------------------------- 1 | eps = 0.05; 2 | eps2 = 2 * eps; 3 | epsz = [0, 0, -eps]; 4 | 5 | $fn = 80; 6 | 7 | d_outer = 13; 8 | d_outer_top = 13.8; 9 | h = 13.5; 10 | h_cone = 7.5; 11 | 12 | d_screw = 2.5; 13 | screw_y = h/2; 14 | 15 | d_antenna = 10; 16 | 17 | tol = 0.3; 18 | 19 | chamfer = 1; 20 | slot_w = 1; 21 | 22 | 23 | d_guide_top = 18+tol; 24 | d_guide_bottom = 20+tol; 25 | d_guide_outside = 24; 26 | 27 | plug(); 28 | //translate([d_outer*1.5, 0, 0]) 29 | //mirror([0, 0, 1]) drill_guide(); 30 | 31 | module plug() { 32 | difference() { 33 | union() { 34 | cylinder(d=d_outer-tol, h=h); 35 | cylinder(d1=d_outer_top-tol, d2=d_outer-tol, h=h_cone); 36 | } 37 | 38 | translate(epsz) cylinder(d=d_antenna+tol, h=h+eps2); 39 | 40 | translate([0, 0, screw_y]) rotate([90, 0, 0]) cylinder(d=d_screw, h=d_outer); 41 | } 42 | } 43 | 44 | module drill_guide() { 45 | rim = 1; 46 | hole_y = rim + d_screw/2; 47 | guide_h = h - screw_y + hole_y; 48 | 49 | w = d_outer/sqrt(2)+0.5; 50 | 51 | 52 | difference() { 53 | cylinder(d=d_guide_outside, h=guide_h); 54 | translate(epsz) cylinder(d1=d_guide_bottom, d2=d_guide_top, h=guide_h+eps2); 55 | translate([0, 0, hole_y]) rotate([90, 0, 0]) cylinder(d=d_screw, h=d_outer); 56 | rotate([0, 0, 45]) translate([-w/2, -w/2, -eps]) cube([w, w, guide_h+eps2]); 57 | rotate([0, 0, 45]) translate(epsz) cube([d_outer, slot_w, guide_h+eps2]); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /transmitter/mechanics/graupner-e8-push-button-mount.scad: -------------------------------------------------------------------------------- 1 | 2 | $fn = 100; 3 | 4 | fudge = 0.05; 5 | fudge2 = 2 * fudge; 6 | fudgez = [0, 0, -fudge]; 7 | 8 | tol = 0.3; 9 | inch = 25.4; 10 | 11 | dim = [12, 12, 7]; 12 | 13 | button_base_dim = [8, 7, 4]; 14 | button_d = 7; 15 | 16 | chamfer_z = dim.z - 3; 17 | pcb_dim = [dim.x+8, dim.y, fudge2]; 18 | 19 | screw_d = 1.7; 20 | 21 | difference() { 22 | union() { 23 | rounded_cube(dim, 0, "xy"); 24 | hull() { 25 | rounded_cube(pcb_dim, 0, "xy"); 26 | translate([0, 0, chamfer_z]) rounded_cube([dim.x, dim.y, fudge2], 0, "xy"); 27 | } 28 | } 29 | translate(fudgez) { 30 | rounded_cube(button_base_dim, 0, "xy"); 31 | cylinder(d=button_d, h=dim.z+fudge2); 32 | translate([0.3*inch, 0, 0]) cylinder(d=screw_d, h=dim.z+fudge2); 33 | translate([-0.3*inch, 0, 0]) cylinder(d=screw_d, h=dim.z+fudge2); 34 | } 35 | } 36 | 37 | 38 | module rounded_cube(dim, r=3, center="") { 39 | d = 2 * r; 40 | fudge = 0.05; 41 | 42 | x = search("x", center) ? 0 : dim.x/2; 43 | y = search("y", center) ? 0 : dim.y/2; 44 | z = search("z", center) ? 0 : dim.z/2; 45 | translate([x, y, z]) 46 | minkowski() { 47 | cube(dim - [d, d, fudge], true); 48 | cylinder(r=r, h=fudge); 49 | } 50 | } -------------------------------------------------------------------------------- /transmitter/mechanics/graupner-e8-switch-plate.scad: -------------------------------------------------------------------------------- 1 | 2 | $fn = 100; 3 | 4 | fudge = 0.05; 5 | fudge2 = 2 * fudge; 6 | fudgez = [0, 0, -fudge]; 7 | 8 | tol = 0.3; 9 | 10 | dim = [59, 15, 0.5]; 11 | d1 = 6.4 - tol; 12 | d2 = 8.2 - tol; 13 | d_switch = 6.4 + tol; 14 | plug_z = 3 + dim.z; 15 | r = 1; 16 | 17 | x_switch1 = 5; 18 | x_switch2 = 20; 19 | x_switch3 = 48; 20 | 21 | difference() { 22 | union() { 23 | rounded_cube(dim, r, "y"); 24 | translate([x_switch1, 0, 0]) cylinder(d1=d1, d2=d1-1, h=plug_z); 25 | translate([x_switch3, 0, 0]) cylinder(d1=d2, d2=d2-1, h=plug_z); 26 | } 27 | translate([x_switch2, 0, -fudge]) cylinder(d=d1, h=plug_z); 28 | } 29 | 30 | 31 | module centered_cube(dim, center="") { 32 | x = search("x", center) ? 0 : dim.x/2; 33 | y = search("y", center) ? 0 : dim.y/2; 34 | z = search("z", center) ? 0 : dim.z/2; 35 | translate([x, y, z]) 36 | cube(dim, true); 37 | } 38 | 39 | module rounded_cube(dim, r=3, center="") { 40 | d = 2 * r; 41 | fudge = 0.05; 42 | 43 | x = search("x", center) ? 0 : dim.x/2; 44 | y = search("y", center) ? 0 : dim.y/2; 45 | z = search("z", center) ? 0 : dim.z/2; 46 | translate([x, y, z]) 47 | minkowski() { 48 | cube(dim - [d, d, fudge], true); 49 | cylinder(r=r, h=fudge); 50 | } 51 | } -------------------------------------------------------------------------------- /transmitter/mechanics/graupner-e8-usb-mount.scad: -------------------------------------------------------------------------------- 1 | 2 | $fn = 100; 3 | 4 | fudge = 0.05; 5 | fudge2 = 2 * fudge; 6 | fudgez = [0, 0, -fudge]; 7 | 8 | tol = 0.3; 9 | inch = 25.4; 10 | 11 | pcb_t = 1.5; 12 | 13 | //dim1 = [17, 8.5-pcb_t, 1]; 14 | dim2 = [13.5, 6.2-pcb_t, 5]; 15 | dim3 = [13.5, 6.2-4, 6.7]; 16 | pos3 = [0, dim2.y-dim3.y, 0]; 17 | 18 | //centered_cube(dim1, center="x"); 19 | centered_cube(dim2, center="x"); 20 | translate(pos3) centered_cube(dim3, center="x"); 21 | translate(pos3+[0, 0, dim3.z]) centered_cube([dim3.x, dim3.y+0.7, 0.5], center="x"); 22 | 23 | module centered_cube(dim, center="") { 24 | x = search("x", center) ? 0 : dim.x/2; 25 | y = search("y", center) ? 0 : dim.y/2; 26 | z = search("z", center) ? 0 : dim.z/2; 27 | translate([x, y, z]) 28 | cube(dim, true); 29 | } -------------------------------------------------------------------------------- /transmitter/windows7-driver/amd64/WUDFUpdate_01009.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/windows7-driver/amd64/WUDFUpdate_01009.dll -------------------------------------------------------------------------------- /transmitter/windows7-driver/amd64/WdfCoInstaller01009.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/windows7-driver/amd64/WdfCoInstaller01009.dll -------------------------------------------------------------------------------- /transmitter/windows7-driver/amd64/winusbcoinstaller2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/windows7-driver/amd64/winusbcoinstaller2.dll -------------------------------------------------------------------------------- /transmitter/windows7-driver/rc-headless-tx.inf: -------------------------------------------------------------------------------- 1 | ; ================ Version section ================= 2 | 3 | [Version] 4 | Signature="$Windows NT$" 5 | Class=USB 6 | ClassGuid={88bae032-5a81-49f0-bc3d-a4ff138216d6} 7 | Provider = %ProviderName% 8 | DriverVer = 03/04/2020, 1.0.0.0 9 | 10 | ; ========== Manufacturer/Models sections =========== 11 | 12 | [Manufacturer] 13 | %ProviderName% = MyWinUSBDevice,NTx86,NTamd64 14 | 15 | [MyWinUSBDevice.NTx86] 16 | %DeviceDesc%=USB_Install, USB\VID_6666&PID_eaf1 17 | 18 | [MyWinUSBDevice.NTamd64] 19 | %DeviceDesc%=USB_Install, USB\VID_6666&PID_eaf1 20 | ; ================== Installation ================== 21 | [ClassInstall] 22 | AddReg=ClassAddReg 23 | 24 | [ClassInstall32] 25 | AddReg=ClassAddReg 26 | 27 | [ClassAddReg] 28 | HKR,,,,"%ClassName%" 29 | HKR,,Icon,,-28 30 | 31 | [USB_Install] 32 | Include = WinUSB.inf 33 | Needs = WinUSB.NT 34 | 35 | [USB_Install.Services] 36 | Include = WinUSB.inf 37 | AddService = WinUSB, 0x00000002, WinUSB_ServiceInstall 38 | 39 | [WinUSB_ServiceInstall] 40 | DisplayName = %WinUSB_SvcDesc% 41 | ServiceType = 1 42 | StartType = 3 43 | ErrorControl = 1 44 | ServiceBinary = %12%\WinUSB.sys 45 | 46 | [USB_Install.Wdf] 47 | KmdfService = WinUSB, WinUSB_Install 48 | 49 | [WinUSB_Install] 50 | KmdfLibraryVersion = 1.11 51 | 52 | [USB_Install.HW] 53 | AddReg = Dev_AddReg 54 | 55 | [Dev_AddReg] 56 | HKR,,DeviceInterfaceGUIDs,0x00010000,"{13eb360b-bc1e-46cb-ac8b-ef3da47b4062}" 57 | 58 | [USB_Install.CoInstallers] 59 | AddReg = CoInstallers_AddReg 60 | CopyFiles = CoInstallers_CopyFiles 61 | 62 | [CoInstallers_AddReg] 63 | HKR, , CoInstallers32, 0x00010000, "WinUSBCoInstaller2.dll","WdfCoInstaller01009.dll, WdfCoInstaller" 64 | 65 | [CoInstallers_CopyFiles] 66 | WinUSBCoInstaller2.dll 67 | WdfCoInstaller01009.dll 68 | 69 | 70 | [SourceDisksNames] 71 | 1 = %MediaDescription% 72 | 73 | [SourceDisksFiles] 74 | WinUSBCoInstaller2.dll = 1, x86 75 | WdfCoInstaller01009.dll = 1, x86 76 | 77 | 78 | [SourceDisksFiles.amd64] 79 | WinUSBCoInstaller2.dll = 1, amd64 80 | WdfCoInstaller01009.dll = 1, amd64 81 | 82 | [DestinationDirs] 83 | CoInstallers_CopyFiles = 11 84 | 85 | ; ==================== Strings ===================== 86 | [Strings] 87 | ProviderName="LANE Boys RC" 88 | 89 | DeviceDesc="RC Headless Tx" 90 | MediaDescription = "RC Headless Tx Driver Disc" 91 | WinUSB_SvcDesc = "WinUSB Driver Service" 92 | -------------------------------------------------------------------------------- /transmitter/windows7-driver/x86/WUDFUpdate_01009.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/windows7-driver/x86/WUDFUpdate_01009.dll -------------------------------------------------------------------------------- /transmitter/windows7-driver/x86/WdfCoInstaller01009.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/windows7-driver/x86/WdfCoInstaller01009.dll -------------------------------------------------------------------------------- /transmitter/windows7-driver/x86/winusbcoinstaller2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laneboysrc/rc-headless-transmitter/9a88acda7804c00078a4e9a39d49c0643950f579/transmitter/windows7-driver/x86/winusbcoinstaller2.dll --------------------------------------------------------------------------------