A color changing desk lamp using ws2801 strip LEDs and a ch340g USB serial adapter

ws2801_strip

I’ve been using Redshift on my desktop for awhile now and have wanted my keyboard light to change color at night as well.  I’d been using a small string of x-mas lights because they offered a nicely spread out light that wasn’t excessively bright at night but they weren’t adjustable in color or brightness.

This project requires soldering, including soldering two small wires to the pins of an SOIC-16 chip.  Even with cheap, poor quality tools, it’s possible, though I did use a solder puller to clean up my first attempt.

Ebay and China sellers have shorter 5 volt ws2801 led strips for under $5 and the USB to serial adapters are only about 60 cents.   My preference is the ch340g serial adapters over FTDI, they have no history of abusing customers who unknowingly bought clones, and the drivers are built in to Linux.

The ws2801 was chosen over the ws2811B due to the tight timing restrictions of the ws2811B, controlling those is not very practical without a micro-controller which would double the price of this project, so make sure you get the right one.

The Hardware:

  • If your strip has wires attached, identify the input end.  Then identify the colors for each pin.  Mine were Blue = Ground, Red = Data, Green = Clock, and Black = +5V.  You can use a meter or continuity test to find Ground and +5V, but you’ll have to look under the shrink tubing to identify Data and Clock.
  • Solder wires to pins 13 and 14 of the ch340g chip on the USB serial adapter.  13 will be Data and 14 will be Clock.  If you’re using the stranded wires that came with your LED strip, you may have to remove a few strands to get the wire to fit on the pin.  I was down to about 5 strands for mine.  Twisting and tinning these wires first helps.
  • Solder wires to the Ground and +5 terminals, also soldering the voltage select terminal to the 5V terminal on the USB serial adapter.CH340G_Adapter
  • Solder all 4 wires to the LED strip (if they weren’t already attached)
  • Cut your strip to length.  Each LED set uses up to 60 milliamps and USB only provides 500 milliamps, so that works out to 8 1/3 RGB LEDs.  I went with 9 for my strip and use nothing else on that port, even though that’s pushing it a little bit.

The software:

Save this code as ws2801write.c and compile with:

 gcc -o ws2801write ws2801write.c

ws2801write.c

/* 32 LEDs is 9.6W or 1.92A
500mA USB is 8.33 LEDs

The pixels are connected by a 4-conductor cable.
+5V (Red)
Ground (Blue)
Data (Yellow) -- DTR
Clock (Green) -- RTS

Clock starts low for at least 500uS.

Data is clocked out 24 bits at a time, in Red, Green, Blue order with msb sent first.

Each bit is captured on rising edge of clock.

Data is latched by holding clock pin low for 1 millisecond.

Syntax:
  ws2801write [ -p <device> ] [ -l <string length> ] [ -c D[TR] | R[TS] ] [ -d D[TR] | R[TS] ] <red> <green> <blue> [ <red> <green> <blue> ]
    -p serial device file, defaults to /dev/ttyUSB0
    -l number of pixels in the string, defaults to 64
    -c clock pin, defaults to RTS
    -d data pin, defaults to DTR
    <red> <green> <blue> sequences are decimal 0-255 each and when fewer sequences are provided than
      <string length>, the final sequence is repeated until <string length> after the sequences run out

reference:
LED string: https://www.aliexpress.com/item//32709231420.html
USB->serial: https://www.aliexpress.com/item//32668866076.html
Datasheet: Page 12 of http://longhornengineer.com/github/Appnotes_WS2801_MOSFET/WS2801_Datasheet.pdf .
Additional information:
   end clock: https://github.com/adafruit/Adafruit-WS2801-Library/blob/master/Adafruit_WS2801.cpp
   DTR/RTS in linux: http://xanthium.in/Controlling-RTS-and-DTR-pins-SerialPort-in-Linux
*/

    #include <stdio.h>
    #include <fcntl.h>       /* File Control Definitions           */
    #include <termios.h>     /* POSIX Terminal Control Definitions */
    #include <unistd.h>      /* UNIX Standard Definitions          */
    #include <errno.h>       /* ERROR Number Definitions           */
    #include <sys/ioctl.h>   /* ioctl()                            */
    #include <stdlib.h>
    #include <stdint.h>
    #include <time.h>

    void sendByte(int fd, uint8_t byte, int dataPin, int clockPin);
    void sleepuS(unsigned long uS);
    int main(int argc, char *argv[]) {
        int fd=-1;
        char *deviceName = "/dev/ttyUSB0";
        unsigned int sLength = 64;
        int clockPin = TIOCM_RTS;
        int dataPin = TIOCM_DTR;
        unsigned int curPixel = 0;
        unsigned int arg;
        int red=255;
        int green=255;
        int blue=255;

        for( arg = 1; arg < argc; arg++ ) {
            switch( argv[arg][0] ) {
                case '-' : // got a command line switch, identify and do it
                    switch( argv[arg][1] ) {
                        case 'p' :
                            deviceName=argv[++arg];
                            break;
                        case 'l' :
                            sLength = atoi(argv[++arg]);
                            break;
                        case 'c' :
                            if( argv[++arg][0]=='R' ) {
                                clockPin = TIOCM_RTS;
                                dataPin = TIOCM_DTR;
                            } else if( argv[arg][0]=='D' )  {
                                clockPin = TIOCM_DTR;
                                dataPin = TIOCM_RTS;
                            } else {
                                printf( "Invalid option: -c %s\n", argv[arg] );
                            }
                            break;
                        case 'd' :
                            if( argv[++arg][0]=='D' ) {
                                clockPin = TIOCM_RTS;
                                dataPin = TIOCM_DTR;
                            } else if( argv[arg][0]=='R' )  {
                                clockPin = TIOCM_DTR;
                                dataPin = TIOCM_RTS;
                            } else {
                                printf( "Invalid option: -d %s\n", argv[arg] );
                            }
                            break;
                        default :
                            printf( "Invalid option: %s\n", argv[arg] );
                            exit(-1);
                    }
                    break;
                case '0' :
                case '1' :
                case '2' :
                case '3' :
                case '4' :
                case '5' :
                case '6' :
                case '7' :
                case '8' :
                case '9' : // got a number, send the colors
                    if( fd == -1 ) {
                        fd = open( deviceName, O_RDWR | O_NOCTTY );
                        if(fd == -1) {                                   /* Error Checking */
                            printf("Error Opening %s\n", deviceName );
                            exit(-2);
                        }
                        ioctl(fd,TIOCMBIS,&clockPin); //set clock low to init
                        sleepuS(600);
                    }
                    red=green=blue=0;
                    if(arg < argc) red=atoi(argv[arg++]);
                    if(arg < argc) green=atoi(argv[arg++]);
                    if(arg < argc) blue=atoi(argv[arg]);
                    sendByte(fd, red, dataPin, clockPin);
                    sendByte(fd, blue, dataPin, clockPin); //note green and blue reversed on my strip
                    sendByte(fd, green, dataPin, clockPin); //swap these lines if yours are not reversed.
                    sLength--;
                    if( sLength <= 0 ){
                       ioctl(fd,TIOCMBIS,&clockPin); //set clock low to latch
                       sleepuS(600); //wait for latch before closing
                       close(fd);
                       exit(0);
                    }
                    break;
                 default :
                   printf( "Invalid option: %s\n", argv[arg] );
                   exit(-1);
            }
        }
        for( ; sLength > 0; sLength-- ) { //fill the remaining LEDs with the last color given
           sendByte(fd, red, dataPin, clockPin);
           sendByte(fd, blue, dataPin, clockPin); //note green and blue reversed on my strip
           sendByte(fd, green, dataPin, clockPin); //swap these lines if yours are not reversed.
        }
        ioctl(fd,TIOCMBIS,&clockPin); //set clock low to latch
        sleepuS(600); //wait for latch before closing
        close(fd);
    }

    void sendByte(int fd, uint8_t byte, int dataPin, int clockPin) {
    int b;
        for( b=0; b<8; b++){
            if( !!(byte & (1 << ( 7 - b ))) ){
                ioctl(fd,TIOCMBIC,&dataPin);//clear is on
//                printf("1");
            } else {
                ioctl(fd,TIOCMBIS,&dataPin);//set is off
//                printf("0");
            }
            ioctl(fd,TIOCMBIC,&clockPin);//data clocked on rising edge (clear is on)
            ioctl(fd,TIOCMBIS,&clockPin);//return to off
        }
//        printf("\n");
    }

    void sleepuS(unsigned long uS){
        struct timespec ts;
        ts.tv_sec = 0;
        ts.tv_nsec = uS*1000L; //1 uS (1 millionths/1000 billionths)
        nanosleep(&ts, NULL);
    }

The syntax is:

ws2801write [ -p  ] [ -l  ] [ -c D[TR] | R[TS] ] [ -d D[TR] | R[TS] ]    [    ]
    -p serial device file, defaults to /dev/ttyUSB0
    -l number of pixels in the string, defaults to 64
    -c clock pin, defaults to RTS
    -d data pin, defaults to DTR
       sequences are decimal 0-255 each and when fewer sequences are provided than
      , the final sequence is repeated until  after the sequences run out

Examples:   9 full on red LEDs:
./ws2801write -l 9 255

9 bright white (a bit blueish on mine)
./ws2801write -l 9 255 255 255

3 bright blue, 3 bright green, and 3 bright red
./ws2801write 0 0 255 0 0 255 0 0 255 0 255 0 0 255 0 0 255 0 255 0 0 255 0 0 255 0 0

9 dim redish
./ws2801write -l 9 127 80 35

9 medium redish
./ws2801write -l 9 255 200 100

Note, some machines may interrupt the transfer process and cause odd colors.  nice -20 ./ws2801write -l 9 255 255 255 may resolve this OS problem.

DIY Charging a Craftsman C3 Lithium Ion battery pack

C3 Battery
To charge DieHard / Craftsman C3 lithium-ion battery packs apply 21V across the + and – terminals. (rounded end = +, opposite = -)

C3 Connector C3 batteries come in several variations and each one has different requirements to allow charging.   Most variants will need a 22K resistor between the left terminal (when looking with the curve away from you) and the + terminal.

Be sure to use a current limited supply to avoid the possibility of damaging your pack or starting a fire should the internal protection circuit fail.

My model was a 315.PP2025 (130211023) rated 19.2V, 48watt hours purchased in mid 2016 and required the 22K resistor between the left terminal and the + terminal to charge without cutting off after 30 seconds.

22K is red red black black brown for 1% 5 band resistors, and red red orange gold for 5% 4 band resistors.

C3 Circuit For charging in the house, I’ve got a 24V 0.5A wall wart (more of a brick) power supply and an LM317 regulator in a TO220 package.  I used three 10 ohm resistors in parallel to spread the heat out so that I could use 1/4 watt resistors without getting them too hot.  (The LM317 will get too hot to touch, be careful.)  The circuit is to the left and limits current to 375mA which, for my 48W pack, is a nice gentle 6 hours charge from empty and generates no heat in the pack that my IR thermometer can detect.  The LM317 will go up to 1.5A with a big heat sink and seven 5.6 ohm 1/4 watt resistors in parallel, but be careful to not charge packs that are below 5V at this high rate.  The 1.5A charger will fill the big 48W pack in 1 hour 40 minutes.

The 375mA charge rate is safe for even deeply discharged batteries that won’t charge in the Craftsman charger.

Systemd timers replacement for cron jobs, plus Weechat LetsEncrypt certificate renewal and distribution.

systemadm

Systemd is here.  Like it or not, we’re being given a new tool and it’s time to learn how to use it.  Today, we address the systemd timers feature and it’s ability to implement the functions of cron.

This isn’t a beginner topic, this blog assumes you know the command line well and have at least some idea of how Linux boots up.  In addition to covering how to implement a timer, an example script will implement renewal and distribution of an updated LetsEncrypt certificate to multiple Weechat users running on a remote host or VM.

Digging right in: Systemd keeps it’s local configuration in /etc/systemd/system, so we’re going to work from that folder.

cd /etc/systemd/system

The first file we’ll create is the .timer file.  In my case, semidaily-timer.timer because I need to trigger twice a day.  If your timer might be used by multiple tasks, a generic name describing the period is appropriate.   My semidaily-timer.timer file contains:

[Unit]
Description=Semi Daily Timer (runs twice a day at 4:44am and 4:44pm)

[Timer]
OnCalendar=*-*-* 04,16:44:00
Unit=semidaily-timer.target

[Install]
WantedBy=multi-user.target

Hopefully the Description field is self explanatory.

The  [Timer] OnCalendar= field is a bit like crontab but simpler.  It’s made up of date fields separated by – and time fields separated by :.  There is a space between the date and time blocks.  The overall format is <year>-<month>-<day> <hour>:<minute>:<second>.

There can be an optional day of the week before the date, and seconds can include a decimal point to handle smaller time units.  The comma separated values in the hour field of the time block are indicating that our timer will trigger at both of the listed hours, 4am and 4pm.  The odd minute was chosen by a random number generator that rolled a “44”. A .. (dot dot) allows entering a range in a single field, and a / can be used to indicate a repeat interval.

Like cron, * is the wildcard and matches anything in that field as shown in the year, month, and day of my file. In addition, you can trigger based on day of the week.  For example OnCalendar=Mon *-*-* 01:00:00 would trigger at 1AM every Monday.  As usual, more details are in the man page. [1]

[Timer] Unit=semidaily-timer.target links in our .target file (described next.)

Something to note is that AccuracySec= defaults to 1 minute so the timing is only accurate to the minute by default.[2]

The [Install] WantedBy=multi-user.target field indicates that this timer can be started only when the multi-user.target has been reached.  The man page has a lengthy list of possible targets [3] and your distro may create more as they desire.  multi-user.target doesn’t guarantee networking is functioning so ideally we’d verify any connectivity needed in our script at run time.

Up next is the Target file.  Mine is called semidaily-timer.target and contains:

[Unit]
Description=Semi Daily Timer Target
StopWhenUnneeded=yes

Again, the Description field is self explanatory.  the [Unit] StopWhenUnneeded= field simply says that systemd can not run this target if nothing uses it.  This file is just a bit of glue and we’re not going to use anything else that isn’t defaults so it’s short and sweet[4].

The final file is our service file.  Since mine is going to be used to run the LetsEncrypt certificate update, I call mine letsencrypt.service.  The contents are:

[Unit]
Description=checks letsencrypt cert and updates if needed.
Wants=semidaily-timer.timer

[Service]
Type=simple
ExecStart=/usr/local/sbin/ssl-key-update.sh

[Install]
WantedBy=semidaily-timer.target

[Unit] Wants= and [Install] WantedBy= are your .timer and .target files from above. [Service] Type=simple means the ExecStart= is the main process of the service and that there is no need to delay after launching it before running other systemd processes. ExecStart=/usr/local/sbin/ssl-key-update.sh specifies our script to be run[5].

Lastly, we need to enable and start everything.

systemctl enable semidaily-timer.timer
systemctl enable letsencrypt.service
systemctl start semidaily-timer.timer

You can verify the setup started successfully with:

systemctl status semidaily-timer.timer

The output should be similar to:

● semidaily-timer.timer - Semi Daily Timer (runs twice a day at 4:44am and 4:44pm)
 Loaded: loaded (/etc/systemd/system/semidaily-timer.timer; enabled; vendor preset: enabled)
 Active: active (waiting) since Tue 2016-10-04 18:26:36 CDT; 43min ago

Oct 04 18:26:36 elm systemd[1]: Started Semi Daily Timer (runs twice a day at 4:44am and 4:44pm).

You can monitor that the timer triggers with:

journalctl -f -u letsencrypt.service

Active timers can be listed with:

systemctl list-timers

The LetsEncrypt certificate renewal script needs the various parameters at the top set, and to be saved in  /usr/local/sbin/ssl-key-update.sh.  It assumes your weechat users are members of the group weechat on the remote host and that you’re running nginx locally on your Ubuntu Xenial computer and have followed the EFF certbot instructions linked in the references [6] to get your certificate. This script assumes you have passwordless key based ssh login on the remote host/VM while running as the unprivileged user. Weechat does not add users to a group by default, any mechanism that gets a list of users could be substituted for the group member list.

Of course the above systemd timer instructions work for any other task that you wish to automate on a schedule as well.

Update: Weechat 1.7 changes the fifo name from weechat_fifo_<pid> to just weechat_fifo.  Script revised.

The script is:

#!/bin/bash
##run twice daily as root

UNPRIVILEGEDUSER=me
REMOTEUSER=weechat
REMOTEHOST=vm17
REMOTEMOUNT=/home/${UNPRIVILEGEDUSER}/${REMOTEHOST}
WEECHATGROUP=weechat
DOMIAN=mydomain.com

#renew cert
letsencrypt renew

#test cert newer than flag?  yes, process, no, exit
if [[ /etc/letsencrypt/live/${DOMIAN}/flag -ot /etc/letsencrypt/live/${DOMIAN}/cert.pem ]]; then

  echo "Certificate updated."
  #update flag
  touch -r /etc/letsencrypt/live/${DOMIAN}/cert.pem /etc/letsencrypt/live/${DOMIAN}/flag

  #restart nginx
  systemctl restart nginx

  #copy keys to a place unprivileged user can read them
  cp /etc/letsencrypt/live/${DOMIAN}/fullchain.pem /home/${UNPRIVILEGEDUSER}/
  cp /etc/letsencrypt/live/${DOMIAN}/privkey.pem /home/${UNPRIVILEGEDUSER}/

  #temporarily drop privileges to ${UNPRIVILEGEDUSER}
  su ${UNPRIVILEGEDUSER} <<'EOF'

  #mount ${REMOTEHOST} as ${UNPRIVILEGEDUSER} if needed (for sshkey)
  if [ -d ${REMOTEMOUNT}/home ]; then
    mounted=true
  else
    mounted=false
    sshfs ${REMOTEUSER}@${REMOTEHOST}:/ ${REMOTEMOUNT}
  fi

  #for each user
  for u in $(ssh ${REMOTEUSER}@${REMOTEHOST} "getent group ${WEECHATGROUP}" | cut -f4 -d:| tr ',' ' '); do

    echo "Processing $u..."

    #concatenate the key into the file, overwriting the old file.
    cat /home/${UNPRIVILEGEDUSER}/fullchain.pem /home/${UNPRIVILEGEDUSER}/privkey.pem > ~/${REMOTEUSER}/home/$u/.weechat/ssl/relay.pem

    #use fifo to set relay.network.ssl_cert_key to a dummy value then back to relay.pem to force a re-read
    ssh ${REMOTEUSER}@${REMOTEHOST} "sudo bash -c \"echo \\\"*/set relay.network.ssl_cert_key \\\"%h/ssl/relay.self.pem\\\"\\\" > /home/$u/.weechat/weechat_fifo\""
    ssh ${REMOTEUSER}@${REMOTEHOST} "sudo bash -c \"echo \\\"*/set relay.network.ssl_cert_key \\\"%h/ssl/relay.pem\\\"\\\" > /home/$u/.weechat/weechat_fifo\""

  done

  #dismount ${REMOTEHOST}i if needed
  if [ "$mounted" = "false ]; then
    fusermount -u ${REMOTEMOUNT}
  fi

  #return to root (EOF must be at character 1)
EOF

  #cleanup temp files
  rm /home/${UNPRIVILEGEDUSER}/fullchain.pem
  rm /home/${UNPRIVILEGEDUSER}/privkey.pem

fi

Reference:

man systemd.unit(5)
^[1] man systemd.time(5)
^[2] man systemd.timer(7)
^[3] man systemd.special(5)
^[4] man systemd.target(5)
^[5] man systemd.service(5)
^[6] https://certbot.eff.org/#ubuntuxenial-nginx

The above directions have been tested on Ubuntu 16.04 with systemd 229.

 

Tortellini Carbonara

Tortellini Carbonara

This is my own creation, it isn’t intended to be an authentic Italian-style Carbonara but more of an Italian inspired dish.

Choose an interesting sausage that you can showcase in this dish.  I prefer to avoid sharp flavors like apple and a cheese sausage would just get lost.

Ingredients:

1 pkg. Tortellini
3 to 5 slices bacon
4 to 6 Oz Sausage, sliced or Ham, diced
2 Tbs olive oil
1/2 cup onion, chopped
1/3 cup celery, chopped
1/4 cup carrot, very finely diced, about 1/16th” cubes
3 cloves garlic, diced
4 oz button mushrooms, sliced into quarters
1 tsp ground black pepper
1 tsp parsley
1/4 cup heavy cream
1 cup milk
1 Tbs roux (Roux Directions)
1 large egg, beaten
2/3 cup shredded Parmesan cheese
1/3 cup shredded mozzarella cheese
1/2 cup frozen or lightly cooked peas

Directions:

Prepare tortellini per the package’s directions.
Cut bacon into 1″ pieces, Bake at 350 until done but not crisp.  Drain and set aside on paper towel.
Sauté Sausage/Ham until lightly browned. Drain and set aside on paper towel.
Sauté onions in olive oil until lightly wilted, add celery and carrot and continue cooking over low heat.
Add Garlic and mushrooms and sauté lightly.
Stir in parsley, pepper and allow veggies to cool.
Add cream, milk, roux and egg, stir completely incorporating the egg.
Slowly add cheeses while cooking over low heat, stirring constantly until egg is fully cooked.
Stir in peas and bacon and allow to thicken before serving on cooked tortellini.

Notes:

Serves 3-4 depending on how much sauce you use per dish.

This is my own creation, it isn’t intended to be an authentic Italian-style Carbonara but more of an Italian inspired dish.

Choose an interesting sausage that you can showcase in this dish.  I prefer to avoid sharp flavors like apple and a cheese sausage would just get lost.

Most of the ingredients can be frozen, but the completed sauce does not freeze very well.  To reheat, add a dash of milk and stir frequently.  Do not combine sauce and tortellini for storage.

 

Why is my weather radar image out of date?

RADAR image

Clicking on NOAA’s radar images gives you weather that ranges from 5 minutes out of date to around 70 minutes out of date.  Ever wonder why such a range and why they’d give us weather images that are so old?

A little while back, I was working for a major retailer and had the opportunity to see how Content Delivery Networks (CDNs) were used in the real world, and just what sorts of problems they caused.  It turns out that NOAA uses a CDN called Akamai and that they’ve mis-configured it so that static content and quickly changing content are all cached for the same time rather than allowing the dynamic content to be handled specially.  To top that off, their complaint email address is uninterested in resolving the problem.  In the several years since I noticed the solution, they have improved the most local radar images so that they are only caching about 3 times longer than they should and they have still not implemented an update trigger system that cause the CDN to get new content as soon as it is available.

There is good news.  Due to the simplistic way NOAA is using their CDN, we can force the CDN to poll for updated content just for us by adjusting the URL slightly.  The full national radar loop is normally accessed via http://radar.weather.gov/Conus/full_loop.php, but by changing that to http://radar.weather.gov/Conus/full_loop.php?x=38, where the “38” is the current minute then the image will update every 10 minutes on the 8s.  The radar is still slightly out of date, but only because it takes a few minutes for NOAA to actually process the data and build the images for the Web and not because of CDN caching problems.  This works because Akamai sees the ?x=38 part and considers the complete URL to be different and therefore polls NOAA for updated data.  Adding ?x=38 (again 38 is just an example, use the current minute) to the end of any noaa.gov or weather.gov URL that should update but doesn’t, will resolve the problem.

If you’d like the problem fully resolved, contact your congressman and tell them that NOAA is wasting money by updating their weather images but not pushing those updates to Akamai so that you can see them.

Speeding up the ADC on an Arduino ATMega 328P

ADC steps
courtesy Spinningspark at Wikipedia

This is a continuation of the performance enhancements for reading the ADC on Arduino.  Similar steps apply to many non Arduino/ATMega processors.

That other article is here: Arduino Library Functions & Macros and introduces startSample(), sampleDone(), and getSampleResult() as a way of avoiding blocking when doing an analogRead.  Here we will update the second two functions and introduce a new function: startFreeRunningADC().

This time we delve a little deeper into the ATMega328 and 168, we implement the free running mode and increase the clock rate to achieve 38,462 samples per second compared to the 8,966 samples per second that analogRead() can do.   Other than the example, none of this is Arduino dependent.  It works on any microcontroller in the same series as the ATMega328 and similar steps can be done on just about any microcontroller with a successive approximation ADC.

The first step to improve performance is to have the ADC operate in Free Running mode, constantly sampling and updating the ADC results registers.  The first conversion is started by calling startFreeRunningADC() from setup().  In this mode the ADC will perform successive conversions every 13 ADC clocks indefinitely instead of doing one conversion in 25 ADC clocks and then waiting to be restarted.  Note, the sampleDone() and getSampleResult() are also slightly updated.  This alone more than doubles performance.

The remaining step is to increase the ADC clock speed from Arduino’s default of 125KHz to 500KHz.  This has a side effect of reducing the sample time which reduces resolution.  To minimize the impact of the reduced sample time, avoid high value resistor dividers and use the lowest value that your signal source can accept, or buffer the signal with an opamp.  There is a compromise speed commented out if you’d prefer the accuracy of remaining close to the 200KHz required for best resolution.  With Free Running mode and a 500KHz ADC clock, the ADC runs at 38,462 samples per second.

Finally, not implemented here, you can trigger an interrupt on conversion complete to avoid polling for results as is implemented here.  In many interrupt driven programs, the ADC results are simply stored and then the stored result is polled, leading to no real improvements in speed.

Here are the functions:

#include "wiring_private.h"
#include "pins_arduino.h"

void startFreeRunningADC(uint8_t pin){
 ADMUX=(1<<REFS0)|((pin-14)&0x07); //select AVCC as reference and set MUX to pin
 ADCSRB = 0; //set free running mode
ADCSRA=(1<<ADEN)|(1<< ADATE)|(1<<ADPS2)|(1<<ADPS0); //set adc clock to 500Khz, enable, free running mode
//ADCSRA=(1<<ADEN)|(1<< ADATE)|(1<<ADPS2)|(1<<ADPS1); //set adc clock to 250Khz, enable, free running mode
 sbi(ADCSRA, ADSC); //start the ADC
}

inline uint8_t sampleDone(){
 // ADIF is set when the conversion finishes
 return bit_is_set(ADCSRA, ADIF);
}

inline uint16_t getSampleResult(){
 uint8_t low, high;
 low = ADCL; //make sure to read L value first
 high = ADCH;
 sbi(ADCSRA, ADIF); //clear conversion complete flag
 return (high << 8) | low;
}

Here is a sample program using these functions:

#define ADCport A4
void setup() {
  Serial.begin(230400); //the ADC is so fast that we need faster serial!
  startFreeRunningADC(ADCport); //free running mode, only needs to start once.
}

void loop() {
  if( sampleDone()){
    Serial.println(getSampleResult());
  }
}

OpenOCD for programming nRF51822 via nothing but wires and a Raspberry Pi

nRF51822 BoardThe Hardware here is simple and cheap, it can be done with any Pi, though I used a Pi Zero.  For me, it was free because I had the Pi Zero and the wire.

The default pins are:

Ground                        to Ground
3.3V power              to 3.3V power
Pi header pin 22  to swclk
Pi header pin 18   to swdio

At the time this was written, OpenOCD didn’t have bcm2835gpio in the main release so we git clone it.
The debugger/programmer install is:

cd ~/Downloads
sudo bash
apt-get install libtool libusb-dev libusb-1.0 autoconf automake texinfo
git clone git://git.code.sf.net/p/openocd/code openocd-code
cd openocd-code/
./bootstrap
./configure --enable-sysfsgpio --enable-bcm2835gpio
make
make install

The raspberrypi-native.cfg didn’t have SWD support, so add them from raspberrypi2-native.cfg.  I needed to comment out:

#bcm2835gpio_jtag_nums 11 25 10 9

and to add:

# Each of the SWD lines need a gpio number set: swclk swdio
# Header pin numbers: 22 18
bcm2835gpio_swd_nums 25 24

The peripheral base address is different on some Pi models.  You can check yours by grabbing the middle 8 digits from:

hexdump -s4 -n4 -e '"0x" 4/1 "%02X""\n"" "' /proc/device-tree/soc/ranges

Edit it with:

vi /usr/local/share/openocd/scripts/interface/raspberrypi-native.cfg

Check to be sure your default speed is 700Mhz, or adjust bcm2835gpio_speed_coeffs:

cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq

To run openocd:

openocd -f interface/raspberrypi-native.cfg -c "transport select swd; set WORKAREASIZE 0" -f target/nrf51.cfg

If it worked correctly, the last line should read (nothing before this indicates you successfully connected):

Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints

To connect from your development platform:

telnet pi0 4444

To burn the code, we need two files, in my case both are on a different machine (host) so I use sshfs to mount that.

mkdir ~/host
sshfs user@host:/ ~/host

Since the path is rather long, I break it in two and store into variables. This is the proximity example from the SDK:

set BASEPATH ~/host/home/user/Downloads/nRF5_SDK_11.0.0_89a8197
set SOFTDEV $BASEPATH/components/softdevice/s130/hex/s130_nrf51_2.0.0_softdevice.hex
set HEXFILE $BASEPATH/examples/ble_peripheral/ble_app_proximity/pca10028/s130/armgcc/_build/nrf51422_xxac_s130.hex
#halt, erase, write softdevice and binary, verify both, run it.
halt; nrf51 mass_erase; sleep 500; flash write_image $SOFTDEV 0; flash write_image $HEXFILE 0; verify_image $SOFTDEV 0; verify_image $HEXFILE 0; reset run

At this point, your sniffer or phone or whatever should see the Nordic_Prox device.