diff --git a/gpio/CHIP_pro/index.html b/gpio/CHIP_pro/index.html
index 0753d55fe2e77691eda26c31210664f1a82a6414..cf6979ff42e5fa7a3163731f845bf74fc75cc93a 100644
--- a/gpio/CHIP_pro/index.html
+++ b/gpio/CHIP_pro/index.html
@@ -45,7 +45,7 @@ done
 </pre>
 
 
-<p><a href='../index.html'>Back</a></p>
+<p><a href='../../index.html'>Back</a></p>
 
 </body>
 
diff --git a/gpio/RaspberryPiZero_bcm2835/index.html b/gpio/RaspberryPiZero_bcm2835/index.html
index c6c0036e688bda4f9884a41ca792b7147d509803..b54dff59547898c0f44aa6834434b7859e715d3c 100644
--- a/gpio/RaspberryPiZero_bcm2835/index.html
+++ b/gpio/RaspberryPiZero_bcm2835/index.html
@@ -69,7 +69,7 @@ int main(int argc, char **argv)
 
 <p>Note: This test was run on a RPiZero W, instead of the baseline version (without wifi).</p>
 
-<p><a href='../index.html'>Back</a></p>
+<p><a href='../../index.html'>Back</a></p>
 
 </body>
 
diff --git a/gpio/RaspberryPiZero_node_rpio/index.html b/gpio/RaspberryPiZero_node_rpio/index.html
index b28591320ae13770bd6856062eba6b94628705c7..d582c55977c82714503db02333cc7e8052bcd554 100644
--- a/gpio/RaspberryPiZero_node_rpio/index.html
+++ b/gpio/RaspberryPiZero_node_rpio/index.html
@@ -33,7 +33,7 @@ while(1){rpio.write(3, 1-rpio.read(5) ); }
 
 <p>Note: This test was run on a RPiZero W, instead of the baseline version (without wifi).</p>
 
-<p><a href='../index.html'>Back</a></p>
+<p><a href='../../index.html'>Back</a></p>
 
 </body>
 
diff --git a/gpio/nrf52/index.html b/gpio/nrf52/index.html
index 3524d92abc385d1f8800dcacf0b43f3012ad04df..0908ddb4e9090857e752b549d8243e14527f2278 100644
--- a/gpio/nrf52/index.html
+++ b/gpio/nrf52/index.html
@@ -65,7 +65,7 @@ void loop() {
 </code>
 </pre>
 
-<p><a href='../index.html'>Back</a></p>
+<p><a href='../../index.html'>Back</a></p>
 
 </body>
 
diff --git a/index.html b/index.html
index 44ab69d7b49ba2ecb24ca52aff2d4ccfbfdf6f7e..d20497bbe80e65247b48643df81868c83c2d5faa 100644
--- a/index.html
+++ b/index.html
@@ -51,6 +51,7 @@ function make_graph(div_id,json_key,axis_labels,use_khz){
     height = 600 - margin.top - margin.bottom;
   var x = d3.scale.linear().range([0, width]);
   var y = d3.scale.linear().range([height, 0]);
+  //var y = d3.scale.log().range([height, 0]);
   var color = d3.scale.category10();
   var xAxis = d3.svg.axis().scale(x).orient("bottom").tickSize(-height,0);
   var yAxis = d3.svg.axis().scale(y).orient("left").tickSize(-width,0);
@@ -76,8 +77,12 @@ function make_graph(div_id,json_key,axis_labels,use_khz){
       else return 1/p;
     }
 
+    //for linear
     x.domain([0,d3.max(data, function(d) { return 1.1*d.dev_board_price; })]).nice();
     y.domain([0,d3.max(data, function(d) { return 1.1*period_to_freq(d.ring_period); })]).nice();
+    //for log
+    //x.domain(d3.extent(data, function(d) { return 1.1*d.dev_board_price; })).nice();
+    //y.domain(d3.extent(data, function(d) { return 1.1*period_to_freq(d.ring_period); })).nice();
 
     var node = svg.selectAll("g").data(data).enter().append("g").append("a")
       .attr("xlink:href", function(d) { return d.subdirectory_path; });
diff --git a/rf/nrf24L01/index.html b/rf/nrf24L01/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..14dbd325a787e8f0e294b864b328ab6c544ee5e6
--- /dev/null
+++ b/rf/nrf24L01/index.html
@@ -0,0 +1,313 @@
+<html>
+<head>
+<style>
+pre code {
+  background-color: #eee;
+  border: 1px solid #999;
+  display: block;
+  padding: 20px;
+}
+figure{
+  text-align: center
+}
+</style>
+
+
+</head>
+<body>
+
+<h1>NRF24L01</h1>
+
+<figure>
+<img src='ring.png' width=50%>
+<figcaption>RF Ring oscillator with NRF24L01.  Note: the cursors are measuring half a cycle.</figcaption>
+</figure>
+
+<p>This RF ring oscillator runs on the NRF24L01 using an Xmega 8E5 microcontroller running at 32 MHz.  C code is available in the linked files (<a href='nrf-ftdi-ring.c'>nrf-ftdi-ring.c</a>, <a href='nrf-ftdi-ring.make'>nrf-ftdi-ring.make</a>, <a href='serial.h'>serial.h</a>), or visible below.</p>
+
+<pre>
+<code>
+#ifndef F_CPU
+#define F_CPU 32000000UL
+#endif
+
+#include "serial.h"
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+
+#define SS_BM PIN4_bm
+#define CE_BM PIN0_bm
+#define IRQ_BM PIN2_bm
+
+//NRF24L01 registers
+//https://www.nordicsemi.com/eng/content/download/2726/34069/file/nrf24L01P_Product_Specification_1_0.pdf
+//Every new command must be started by a high to low transition on CSN.
+const uint8_t CONFIG = 0x00;
+const uint8_t EN_AA = 0x01;
+const uint8_t EN_RXADDR = 0x02;
+const uint8_t SETUP_AW = 0x03;
+const uint8_t SETUP_RETR = 0x04;
+const uint8_t RF_CH = 0x05;
+const uint8_t RF_SETUP = 0x06;
+const uint8_t STATUS = 0x07;
+const uint8_t OBSERVE_TX = 0x08;
+const uint8_t RPD = 0x09;
+const uint8_t RX_ADDR_P0 = 0x0A;
+const uint8_t RX_ADDR_P1 = 0x0B;
+const uint8_t RX_ADDR_P2 = 0x0C;
+const uint8_t RX_ADDR_P3 = 0x0D;
+const uint8_t RX_ADDR_P4 = 0x0E;
+const uint8_t RX_ADDR_P5 = 0x0F;
+const uint8_t TX_ADDR = 0x10;
+const uint8_t RX_PW_P0 = 0x11;
+const uint8_t RX_PW_P1 = 0x12;
+const uint8_t RX_PW_P2 = 0x13;
+const uint8_t RX_PW_P3 = 0x14;
+const uint8_t RX_PW_P4 = 0x15;
+const uint8_t RX_PW_P5 = 0x16;
+const uint8_t FIFO_STATUS = 0x17;
+const uint8_t DYNPD = 0x1C;
+const uint8_t FEATURE = 0x1D;
+
+const uint8_t PWR_UP = 1 << 1;
+const uint8_t PRIM_RX = 1 << 0;
+const uint8_t R_REGISTER = 0;
+const uint8_t W_REGISTER = 1<<5;
+const uint8_t R_RX_PAYLOAD = (1<<6) | (1<<5) | 1;
+const uint8_t W_TX_PAYLOAD = (1<<7) | (1<<5);
+const uint8_t FLUSH_TX = (1<<7) | (1<<6) | (1<<5) | 1;
+const uint8_t FLUSH_RX = (1<<7) | (1<<6) | (1<<5) | (1<<1);
+const uint8_t MAX_RT = 1<<4;
+const uint8_t TX_DS = 1<<5; //tx data sent interrupt
+const uint8_t RX_DR = 1<<6; //rx data ready interrupt
+
+
+uint8_t read_register(uint8_t reg){
+   PORTC.OUTCLR = SS_BM; //SS low      
+   _delay_us(1);  //give time after ss low 
+   SPIC.DATA = R_REGISTER | reg; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   SPIC.DATA = 0; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   uint8_t temp = SPIC.DATA;
+   PORTC.OUTSET = SS_BM; //SS high
+   _delay_us(1);  //give time after ss high
+   return temp;
+}
+void write_register(uint8_t reg, uint8_t val){
+   //must be in standby mode before calling this function!
+   PORTC.OUTCLR = SS_BM; //SS low 
+   _delay_us(1);     
+   SPIC.DATA = W_REGISTER | reg; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   SPIC.DATA = val ; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   PORTC.OUTSET = SS_BM; //SS high   
+   _delay_us(1);
+}
+
+USART_data_t USART_data;
+
+uint8_t token = 0; //token to pass
+uint8_t tempval = 0;
+const int pll_delay_us = 130;
+const int ce_delay_us = 10;
+
+void check_registers(uint8_t id){
+   //for debug only
+   //enter standby so we can write to configuration register.
+   PORTC.OUTCLR = CE_BM; 
+   _delay_us(ce_delay_us);         
+
+   PORTC.OUTCLR = SS_BM; //SS low  
+   _delay_us(10);            
+   SPIC.DATA = R_REGISTER | CONFIG; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   uint8_t status = SPIC.DATA;
+   SPIC.DATA = 0; while(!(SPIC.STATUS & SPI_IF_bm)) {};  //read
+   uint8_t config = SPIC.DATA;
+   PORTC.OUTSET = SS_BM; //SS high
+   _delay_us(10); 
+
+   usart_send_byte(&USART_data,id);
+   usart_send_byte(&USART_data,status);
+   usart_send_byte(&USART_data,config);
+   usart_send_byte(&USART_data,token);
+   usart_send_byte(&USART_data,10);
+}
+
+void rx_from_standby(){
+   //call this from standby to enter rx mode
+   //tempval = read_register(CONFIG);
+   //write_register(CONFIG, tempval | PRIM_RX);
+   write_register(CONFIG, 0x13); //replaces the spi read
+   //set CE for at least 10 us 
+   PORTC.OUTSET = CE_BM;
+   //_delay_us(ce_delay_us); //is this necessary?
+   //wait for pll to settle
+   _delay_us(pll_delay_us);
+}
+
+void setup(){
+   // set up clock
+   OSC.CTRL = OSC_RC32MEN_bm; // enable 32MHz clock
+   while (!(OSC.STATUS & OSC_RC32MRDY_bm)); // wait for clock to be ready
+   CCP = CCP_IOREG_gc; // enable protected register change
+   CLK.CTRL = CLK_SCLKSEL_RC32M_gc; // switch to 32MHz clock
+
+   //set up usart
+   PORTD.DIRSET = PIN3_bm; //TXD0
+   PORTD.DIRCLR = PIN2_bm; //RXD0
+   USART_InterruptDriver_Initialize(&USART_data, &USARTD0, USART_DREINTLVL_LO_gc);
+   USART_Format_Set(USART_data.usart, USART_CHSIZE_8BIT_gc,
+                     USART_PMODE_DISABLED_gc, 0);
+   USART_RxdInterruptLevel_Set(USART_data.usart, USART_RXCINTLVL_LO_gc);
+   //take f_sysclk/(BSEL+1) ~= f_baud*16 with zero scale.  See manual or spreadsheet for scale defs
+   USART_Baudrate_Set(&USARTD0, 123 , -4); //230400 baud with .08% error
+   USART_Rx_Enable(USART_data.usart);
+   USART_Tx_Enable(USART_data.usart);
+   //enable interrupts
+   PMIC.CTRL |= PMIC_LOLVLEX_bm;
+
+   
+   PORTC.DIRSET = SS_BM; //set up SS pin on PC4
+   PORTC.PIN4CTRL = PORT_OPC_WIREDANDPULL_gc; //wired AND and pull-up on SS
+   PORTC.PIN2CTRL = PORT_OPC_PULLUP_gc; //pull-up on IRQ
+   PORTC.DIRSET = CE_BM; //set up CE pin on PC0
+   //set up spic.ctrl
+   SPIC.CTRL   = SPI_PRESCALER_DIV4_gc |        /* SPI prescaler. */
+                         (1 ? SPI_CLK2X_bm : 0) |    /* SPI Clock double. */
+                         SPI_ENABLE_bm |                 /* Enable SPI module. */
+                         SPI_MASTER_bm |                 /* SPI master. */
+                         SPI_MODE_0_gc; //bits driven at falling edge, sampled at rising edge  
+   SPIC.INTCTRL = SPI_INTLVL_OFF_gc;
+   PORTC.DIRSET = PIN5_bm | PIN7_bm; //mosi and sck as outputs
+   PORTC.OUTSET = SS_BM; //SS high   
+   _delay_ms(5); //warm up
+
+   //nrf config
+   //turn off auto-retransmit
+   write_register(SETUP_RETR,0);
+   //turn off disable auto-acknowledge
+   write_register(EN_AA,0);
+   //set the PWR_UP bit in the CONFIG register to 1 to enter standby mode
+   write_register(CONFIG,PWR_UP);
+   _delay_ms(3);
+   //set up data pipe 0 
+   write_register(EN_RXADDR,1);
+   //set data pipe 0 payload length to 1
+   write_register(RX_PW_P0,1);
+   //set data rate to 2 Mbps with high power
+   //I think there may be a typo in the data sheet here.
+   write_register(RF_SETUP,(0<<5) | (1<<3) | (1<<2) | (1<<1));
+   //flush tx
+   PORTC.OUTCLR = SS_BM; //SS low      
+   SPIC.DATA = FLUSH_TX; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   PORTC.OUTSET = SS_BM; //SS high
+   _delay_ms(1);  //give time to start up 
+   //flush rx
+   PORTC.OUTCLR = SS_BM; //SS low      
+   SPIC.DATA = FLUSH_RX; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   PORTC.OUTSET = SS_BM; //SS high
+   _delay_ms(1);  //give time to start up 
+   // and mask MAX_RT interrupts on IRQ
+   tempval = read_register(CONFIG);
+   write_register(CONFIG,tempval | MAX_RT);
+   //clear all interrupts
+   tempval = read_register(STATUS);
+   write_register(STATUS,tempval | MAX_RT | RX_DR | TX_DS);
+   _delay_ms(1);
+
+   sei();
+   //enter standby so we can write to configuration register.
+   PORTC.OUTCLR = CE_BM; 
+   _delay_us(ce_delay_us);
+   rx_from_standby(); //enter rx mode
+}
+
+
+
+void send_token(){
+   //call this from standby1
+   //put token in tx fifo
+   PORTC.OUTCLR = SS_BM; //SS low
+   _delay_us(1);           
+   SPIC.DATA = W_TX_PAYLOAD; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   tempval = SPIC.DATA; //get status while we have it
+   SPIC.DATA = 0; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   PORTC.OUTSET = SS_BM; //SS high
+   _delay_us(1); 
+
+   //tempval = read_register(CONFIG);
+   write_register(CONFIG, tempval & (~PRIM_RX));
+   //pulse CE 
+   PORTC.OUTSET = CE_BM; _delay_us(ce_delay_us); PORTC.OUTCLR = CE_BM;
+   //wait for pll to settle
+   _delay_us(pll_delay_us);
+   //wait until transmit complete
+   while( PORTC.IN & IRQ_BM ){}
+   //clear IRQ -- need to be in standby.
+   tempval = read_register(STATUS);
+   write_register(STATUS, tempval | TX_DS);
+   //enter standby so we can write to configuration register.
+   rx_from_standby(); //return to RX mode
+}
+
+void read_token(){
+   //call this from rx
+   //get token from rx fifo
+   PORTC.OUTCLR = SS_BM; //SS low
+   _delay_us(1);           
+   SPIC.DATA = R_RX_PAYLOAD; while(!(SPIC.STATUS & SPI_IF_bm)) {};
+   tempval = SPIC.DATA; //get status while we have it
+   SPIC.DATA = 0; while(!(SPIC.STATUS & SPI_IF_bm)) {}; 
+   token = SPIC.DATA;
+   PORTC.OUTSET = SS_BM; //SS high
+   _delay_us(1); 
+
+   //transition from RX to standby1 (CE=0)
+   PORTC.OUTCLR = CE_BM;   
+   _delay_us(ce_delay_us);
+
+   //clear IRQ
+   write_register(STATUS, tempval | RX_DR);
+   //check_registers(66);
+}
+
+int main(void) {
+   setup();
+   while(1){
+      if ( !(PORTC.IN & IRQ_BM)){
+         read_token(); //in standby
+         token += 1; //increment token
+         //check_registers(65);
+         send_token();
+      }
+      //we use a signal on the usart to start the oscillation.
+      if (USART_RXBufferData_Available(&USART_data)) {
+         USART_RXBuffer_GetByte(&USART_data); //clear usart buffer so we only fire once.
+         //transition from RX to standby1 (CE=0)
+         PORTC.OUTCLR = CE_BM;   
+         _delay_us(ce_delay_us);
+         send_token(); 
+         //check_registers(64);
+      }
+      //_delay_ms(10);
+   }
+}
+
+ISR(USARTD0_RXC_vect){USART_RXComplete(&USART_data);}
+ISR(USARTD0_DRE_vect){USART_DataRegEmpty(&USART_data);}
+</code>
+</pre>
+
+<p>Most of the time is spent switching between TX and RX (one switch takes 130 us).</p>
+
+<figure>
+<img src='nrf24-state-diagram.png' width=80%>
+<figcaption>NRF24 Radio Modes.</figcaption>
+</figure>
+
+
+<p><a href='../../index.html'>Back</a></p>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/rf/nrf52832/index.html b/rf/nrf52832/index.html
index d6fdaaa8e794a5ae08224f72597bb0afefa09b93..b67c2f9c8a44b13b13ac23c3e1a8db957f01dbbd 100644
--- a/rf/nrf52832/index.html
+++ b/rf/nrf52832/index.html
@@ -132,7 +132,7 @@ void loop() {}
 
 
 
-<p><a href='../index.html'>Back</a></p>
+<p><a href='../../index.html'>Back</a></p>
 
 </body>