{"id":26680037,"url":"https://github.com/wagiminator/attiny814-nrf2usb","last_synced_at":"2025-10-13T19:38:05.236Z","repository":{"id":89315321,"uuid":"428036991","full_name":"wagiminator/ATtiny814-NRF2USB","owner":"wagiminator","description":"USB Serial Controlled 2.4GHz Transceiver","archived":false,"fork":false,"pushed_at":"2023-02-26T10:34:18.000Z","size":2460,"stargazers_count":13,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-12T11:42:54.345Z","etag":null,"topics":["arduino","attiny","avr","nrf24l01","pcb","tinyavr","usb","wireless"],"latest_commit_sha":null,"homepage":"https://oshwlab.com/wagiminator/attiny814-nrf2usb","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wagiminator.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-11-14T20:44:31.000Z","updated_at":"2024-07-06T14:19:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"b35cfcbf-5a54-4726-b538-273ae7937ed0","html_url":"https://github.com/wagiminator/ATtiny814-NRF2USB","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wagiminator/ATtiny814-NRF2USB","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagiminator%2FATtiny814-NRF2USB","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagiminator%2FATtiny814-NRF2USB/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagiminator%2FATtiny814-NRF2USB/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagiminator%2FATtiny814-NRF2USB/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wagiminator","download_url":"https://codeload.github.com/wagiminator/ATtiny814-NRF2USB/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wagiminator%2FATtiny814-NRF2USB/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279016928,"owners_count":26085888,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","attiny","avr","nrf24l01","pcb","tinyavr","usb","wireless"],"created_at":"2025-03-26T06:19:42.524Z","updated_at":"2025-10-13T19:38:05.231Z","avatar_url":"https://github.com/wagiminator.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# USB2NRF - USB Serial Controlled 2.4GHz Transceiver\nNRF2USB is a simple development tool for wireless applications based on the nRF24L01+ 2.4GHz transceiver module. It provides a serial interface for communication with the module via USB. The CH330N (or CH340N) USB to serial chip can also function as a SerialUPDI programmer for the integrated ATtiny814 (or compatible), so that no external programming device is necessary.\n\n![pic1.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny814-NRF2USB/main/documentation/NRF2USB_pic1.jpg)\n![pic2.jpg](https://raw.githubusercontent.com/wagiminator/ATtiny814-NRF2USB/main/documentation/NRF2USB_pic2.jpg)\n\n# Hardware\nThe wiring is pretty simple:\n\n![wiring.png](https://raw.githubusercontent.com/wagiminator/ATtiny814-NRF2USB/main/documentation/NRF2USB_wiring.png)\n\nThe CH330N can be replaced by a CH340N. With the toggle switch the user can select UART mode for data transfer or UPDI mode for programming the device.\n\n# Software\n## UART Implementation\nThe new tinyAVR are equipped with a hardware module for UART, so implementation is very easy. The internal oscillator is sufficiently accurate. The optional calibration with regard to the supply voltage was use here, although it is probably unnecessary. The receive routine is interrupt-driven and uses a simple ring buffer. For more information on the USART module refer to [Microchip Technical Brief TB3216](https://ww1.microchip.com/downloads/en/Appnotes/TB3216-Getting-Started-with-USART-DS90003216.pdf).\n\n```c\n// UART definitions and macros\n#define UART_BAUD         230400                // UART baud rate (max 1/8 of F_CPU)\n#define UART_BAUD_RATE    8.0 * F_CPU / UART_BAUD + 0.5\n#define UART_ready()      (USART0.STATUS \u0026 USART_DREIF_bm)\n#define UART_available()  (UART_RX_head != UART_RX_tail)\n\n// UART RX buffer and pointer\n#define UART_BUF_LEN      64                    // UART RX buffer length (must be a power of two, max 256)\nvolatile uint8_t UART_RX_buf[UART_BUF_LEN];     // RX ring buffer\nvolatile uint8_t UART_RX_head = 0;              // RX buffer pointer for writing\n         uint8_t UART_RX_tail = 0;              // RX buffer pointer for reading\n\n// UART init\nvoid UART_init(void) {\n  pinOutput(PIN_TXD);                           // set TXD pin to output\n  int8_t sigrow_val = SIGROW_OSC20ERR5V;        // get calibration value\n  int32_t baud_setting = UART_BAUD_RATE;        // calculate baud register value ...\n  baud_setting *= (1024 + sigrow_val);          // ... with error compensation ...\n  baud_setting /= 1024;                         // ... for 5V and 20MHz oscillator\n  USART0.BAUD  = (uint16_t)baud_setting;        // set BAUD\n  USART0.CTRLA = USART_RXCIE_bm;                // enable RX interrupt\n  USART0.CTRLB = USART_RXEN_bm                  // enable RX\n               | USART_TXEN_bm                  // enable TX\n               | USART_RXMODE_CLK2X_gc;         // double speed\n}\n\n// UART transmit data byte\nvoid UART_write(uint8_t data) {\n  while(!UART_ready());                         // wait until ready for next data\n  USART0.TXDATAL = data;                        // send data byte\n}\n\n// UART read data byte from RX buffer\nuint8_t UART_read(void) {\n  while(!UART_available());                     // wait for data to be received\n  UART_RX_tail \u0026= (UART_BUF_LEN - 1);           // limit pointer\n  return UART_RX_buf[UART_RX_tail++];           // read and return data byte\n}\n\n// UART RXC interrupt service routine\nISR(USART0_RXC_vect) {\n  UART_RX_head \u0026= (UART_BUF_LEN - 1);           // limit pointer\n  UART_RX_buf[UART_RX_head++] = USART0.RXDATAL; // write received byte to buffer\n}\n```\n\n## SPI Implementation\nThe nRF24L01+ module is controlled via SPI (Serial Peripheral Interface). Since the new ATtinys also have a hardware module for this, implementation is a piece of cake. The maximum speed (half the MCU clock) is used here. For more information on the SPI module refer to [Microchip Technical Brief TB3215](https://www.microchip.com/content/dam/mchp/documents/MCU08/ApplicationNotes/ApplicationNotes/TB3215-Getting-Started-with-SPI-DS90003215.pdf).\n\n```c\n// SPI init\nvoid SPI_init(void) {\n  pinOutput(PIN_MOSI);                          // set MOSI pin as output\n  pinOutput(PIN_SCK);                           // set SCK pin as output\n  SPI0.CTRLA = SPI_CLK2X_bm                     // double speed\n             | SPI_ENABLE_bm                    // enable SPI\n             | SPI_MASTER_bm                    // master mode\n             | SPI_PRESC_DIV4_gc;               // prescaler 4\n  SPI0.CTRLB = SPI_SSD_bm;                      // disable SS line\n}\n\n// SPI transmit and receive a byte\nuint8_t SPI_transfer(uint8_t data) {\n  SPI0.DATA = data;                             // start exchanging data byte\n  while(~SPI0.INTFLAGS \u0026 SPI_IF_bm);            // wait for transfer to complete\n  return SPI0.DATA;                             // return received byte\n}\n```\n\n## nRF24L01+ Implementation\nThe nRF25L01+ module is controlled by writing and reading its registers via SPI. The corresponding registers, commands and the state diagram can be found in the [datasheet](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf). The important functions are shown below:\n\n```c\n// NRF registers\n#define NRF_REG_CONFIG        0x00              // configuration register\n#define NRF_REG_RF_CH         0x05              // RF frequency channel\n#define NRF_REG_RF_SETUP      0x06              // RF setup register\n#define NRF_REG_STATUS        0x07              // status register\n#define NRF_REG_RX_ADDR_P0    0x0A              // RX address pipe 0\n#define NRF_REG_RX_ADDR_P1    0x0B              // RX address pipe 1\n#define NRF_REG_TX_ADDR       0x10              // TX address\n#define NRF_REG_FIFO_STATUS   0x17              // FIFO status register\n#define NRF_REG_DYNPD         0x1C              // enable dynamic payload length\n#define NRF_REG_FEATURE       0x1D              // feature\n\n// NRF commands\n#define NRF_CMD_R_RX_PL_WID   0x60              // read RX payload length\n#define NRF_CMD_R_RX_PAYLOAD  0x61              // read RX payload\n#define NRF_CMD_W_TX_PAYLOAD  0xA0              // write TX payload\n#define NRF_CMD_FLUSH_TX      0xE1              // flush TX FIFO\n#define NRF_CMD_FLUSH_RX      0xE2              // flush RX FIFO\n\n// NRF global variables\nuint8_t NRF_channel = 0x02;                     // channel (0x00 - 0x7F)\nuint8_t NRF_speed   = 0;                        // 0:250kbps, 1:1Mbps, 2:2Mbps\nuint8_t NRF_tx_buffer[NRF_PAYLOAD];             // transmit buffer\nuint8_t NRF_tx_addr[] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7};\nuint8_t NRF_rx_addr[] = {0xC2, 0xC2, 0xC2, 0xC2, 0xC2};\nconst uint8_t NRF_SETUP[] = {0x26, 0x06, 0x0E};\nconst uint8_t* NRF_STR[]  = {\"250k\", \"1M\", \"2M\"};\n\n// NRF switch to Power Down\nvoid NRF_powerDown(void) {\n  pinLow(PIN_CE);                                       // return to Standby-I\n  NRF_writeRegister(NRF_REG_CONFIG, NRF_CONFIG | 0x00); // !PWR_UP\n}\n\n// NRF switch to RX mode\nvoid NRF_powerRX(void) {\n  pinLow(PIN_CE);                                       // return to Standby-I\n  NRF_writeRegister(NRF_REG_CONFIG, NRF_CONFIG | 0x03); // PWR_UP + PRIM_RX\n  pinHigh(PIN_CE);                                      // switch to RX Mode\n}\n\n// NRF switch to TX mode\nvoid NRF_powerTX(void) {\n  pinLow(PIN_CE);                                       // return to Standby-I\n  NRF_writeRegister(NRF_REG_CONFIG, NRF_CONFIG | 0x02); // PWR_UP + !PRIM_RX\n  pinHigh(PIN_CE);                                      // switch to TX Mode\n}\n\n// NRF configure\nvoid NRF_configure(void) {\n  pinLow(PIN_CE);                                       // leave active mode\n  NRF_writeBuffer(NRF_REG_RX_ADDR_P1, NRF_rx_addr, 5);  // set RX address\n  NRF_writeBuffer(NRF_REG_TX_ADDR,    NRF_tx_addr, 5);  // set TX address\n  NRF_writeBuffer(NRF_REG_RX_ADDR_P0, NRF_tx_addr, 5);  // set TX address for auto-ACK\n  NRF_writeRegister(NRF_REG_RF_CH, NRF_channel);        // set channel\n  NRF_writeRegister(NRF_REG_RF_SETUP, NRF_SETUP[NRF_speed]); // set speed and power\n  NRF_writeRegister(NRF_REG_FEATURE,  0x04);            // enable dynamic payload length\n  NRF_writeRegister(NRF_REG_DYNPD,    0x3F);            // enable dynamic payload length\n  NRF_writeCommand(NRF_CMD_FLUSH_RX);                   // flush RX FIFO\n  NRF_powerRX();                                        // switch to RX Mode\n}\n\n// Check if data is available for reading\nuint8_t NRF_available(void) {\n  if(NRF_readRegister(NRF_REG_STATUS) \u0026 0x40) return 1;\n  return(!(NRF_readRegister(NRF_REG_FIFO_STATUS) \u0026 0x01));\n}\n\n// Read payload bytes and send them via UART\nvoid NRF_to_UART(void) {\n  uint8_t len = NRF_readRegister(NRF_CMD_R_RX_PL_WID);  // read payload length\n  pinLow(PIN_CSN);                                      // start SPI transfer\n  SPI_transfer(NRF_CMD_R_RX_PAYLOAD);                   // read payload command\n  while(len--) UART_write(SPI_transfer(0));             // transfer payload to UART\n  pinHigh(PIN_CSN);                                     // stop SPI transfer\n  NRF_writeRegister(NRF_REG_STATUS, 0x40);              // clear status flags\n}\n\n// Send a data package (max length 32)\nvoid NRF_send(uint8_t *data, uint8_t len) {\n  NRF_writeRegister(NRF_REG_STATUS, 0x30);              // clear status flags\n  NRF_writeCommand(NRF_CMD_FLUSH_TX);                   // flush TX FIFO\n  NRF_writeBuffer(NRF_CMD_W_TX_PAYLOAD, data, len);     // write payload\n  NRF_powerTX();                                        // switch to TX Mode; transmit\n  while(!(NRF_readRegister(NRF_REG_STATUS) \u0026 0x30));    // wait until finished\n  NRF_powerRX();                                        // return to listening\n}\n```\n\n## Main Function\nThe main function brings it all together:\n\n```c\nint main(void) {\n  // Setup MCU\n  _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 1);           // set clock frequency to 10 MHz\n  \n  // Setup\n  uint8_t tx_ptr = 0;                               // NRF TX buffer pointer\n  pinOutput(PIN_LED);                               // set LED pin as output\n  EEPROM_get();                                     // read user settings from EEPROM\n  NRF_init();                                       // setup NRF\n  SPI_init();                                       // setup SPI serial interface\n  UART_init();                                      // setup UART serial interface\n  NRF_configure();                                  // configure NRF\n  sei();\n\n  // Loop\n  while(1) {    \n    if(NRF_available()) {                           // something coming in via NRF?\n      pinHigh(PIN_LED);                             // switch on LED\n      NRF_to_UART();                                // send received payload via UART\n    }\n\n    if(UART_available()) {                          // something coming in via UART?\n      uint8_t c = UART_read();                      // read the character ...\n      NRF_tx_buffer[tx_ptr++] = c;                  // ... and write it to the buffer\n      if((tx_ptr == NRF_PAYLOAD) || (c == '\\n')) {  // buffer full or new line?\n        if(NRF_tx_buffer[0] == CMD_IDENT) parse();  // is it a command? -\u003e parse\n        else {                                      // not a command?\n          pinHigh(PIN_LED);                         // switch on LED\n          NRF_send(NRF_tx_buffer, tx_ptr);          // send the buffer via NRF\n        }\n        tx_ptr = 0;                                 // reset buffer pointer\n      }\n    }\n\n    pinLow(PIN_LED);                                // switch off LED\n  }\n}\n```\n\n## Compiling and Uploading\n- Set the selector switch on the device to UPDI. \n- Plug the device into a USB port of your PC.\n\n### If using the Arduino IDE\n- Open your Arduino IDE.\n- Make sure you have installed [megaTinyCore](https://github.com/SpenceKonde/megaTinyCore).\n- Go to **Tools -\u003e Board -\u003e megaTinyCore** and select **ATtiny1614/1604/814/804/414/404/214/204**.\n- Go to **Tools** and choose the following board options:\n  - **Chip:**           Any chip should work\n  - **Clock:**          10 MHz internal\n  - **Programmer:**     SerialUPDI (230400 BAUD)\n  - Leave the rest at the default settings.\n- Go to **Tools -\u003e Burn Bootloader** to burn the fuses.\n- Open USB2NRF sketch and click **Upload**.\n- Set the selector switch on the device back to UART.\n\n### If using the makefile (Linux/Mac)\n- Make sure you have installed the latest [avr-gcc toolchain](http://maxembedded.com/2015/06/setting-up-avr-gcc-toolchain-on-linux-and-mac-os-x/).\n- Open a terminal.\n- Navigate to the folder with the makefile and the Arduino sketch.\n- Run `DEVICE=attiny814 make install` to compile, burn the fuses and upload the firmware (change DEVICE accordingly).\n- Set the selector switch on the device back to UART.\n\n# Operating Instructions\nSet the selector switch on the device to UART. Plug the device into a USB port of your PC. Open a serial monitor and set it to 230400 BAUD.\n\nEnter the text to be sent, terminated with a Newline (NL or '\\ n'). A string that begins with an exclamation mark ('!') is recognized as a command. The command is given by the letter following the exclamation mark. Command arguments are appended as bytes in 2-digit hexadecimal directly after the command. The following commands can be used to set the NRF:\n\n|Command|Description|Example|Example Description|\n|-|:-|:-|:-|\n|c|set channel|!c2A|set channel to 0x2A (0x00 - 0x7F)|\n|t|set TX address|!t7B271F1F1F|addresses are 5 bytes, LSB first|\n|r|set RX address|!t41C355AA55|addresses are 5 bytes, LSB first|\n|s|set speed|!s02|data rate (00:250kbps, 01:1Mbps, 02:2Mbps)|\n\nEnter just the exclamation mark ('!') for the actual NRF settings to be printed in the serial monitor. The selected settings are saved in the EEPROM and are retained even after a restart.\n\n# References, Links and Notes\n1. [ATtiny814 Datasheet](https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny417-814-816-817-DataSheet-DS40002288A.pdf)\n2. [nRF24L01+ Datasheet](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf)\n3. [Microchip Technical Brief TB3215](https://www.microchip.com/content/dam/mchp/documents/MCU08/ApplicationNotes/ApplicationNotes/TB3215-Getting-Started-with-SPI-DS90003215.pdf)\n4. [Microchip Technical Brief TB3216](https://ww1.microchip.com/downloads/en/Appnotes/TB3216-Getting-Started-with-USART-DS90003216.pdf)\n\n\n# License\n![license.png](https://i.creativecommons.org/l/by-sa/3.0/88x31.png)\n\nThis work is licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License. \n(http://creativecommons.org/licenses/by-sa/3.0/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwagiminator%2Fattiny814-nrf2usb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwagiminator%2Fattiny814-nrf2usb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwagiminator%2Fattiny814-nrf2usb/lists"}