MagicBox Automatic T/R Switch for QRP Radios

I’m ashamed to say that I’ve had this kit in the manila envelope since I purchased it three years ago. I’m finally at the point where I could really use the capability of switching in/out a transmitter. This is catalyzed by playing with the SDRplay and thinking about using it as half a transceiver.

The kit is straightforward thru-hole assembly. I’m thinking I will probably outsource the drilling of the front panel to a friend who has better accuracy than me 🙂

Pcduino Cluster Part 1

I used to do a lot [more] HPC software development when I had access to the Penguin cluster at a previous place of employment. The cluster was a full upright rack full of nodes.

As I get more into node.js (and some other stuff: you can do MPI in python), I decided to build a small cluster for development purposes. After my really good initial experiences with a pcduino3, I decided to use the pcduino3 nano instead of raspberry pi’s. The pcduino3 has a lot more power than the rpi and you can find it for about the same price if you shop sales (checkout LinkSprite).

With the proliferation of cheap gigabit switches and high amperage multi-port USB chargers (like the EZOPower 96 watt 8-Port) you can build a fairly affordable low end cluster that is not very big.

IMG_0470.JPG

Sparkfun geiger counter

Super short weekend project: hookup my Sparkfun geiger counter and start reading counts per minute.

With the protective cap on (the tube will only read gamma and beta radiation) I see 6 to 16 counts per minute. I do not have a radiation source to calculate the conversion coefficient to micro Sieverts.

On a side note, I attached the board on a VMware ESXi machine and read the raw data with a python script. I’ll create a feed here soon.

Flying PigRig #316

Finished up the transceiver and ran it through the alignment process. My frequency counter nor my signal generator are accurate to enough decimal places. I ended up aligning it by ear. The oscillator output is a nice clean sign wave. The output looks excellent after a quick look at the output on spectrum analyzer.

Mounted in a case. The included copper heat sink appears to be quite adequate. Still having thoughts about what type of power cable to use. My preference is Anderson PowerPole, but there are not a lot of chassis mounting options. I’m considering ordering some Anderson AutoGrip blocks from Hardened Power Systems.

20131226-211106.jpg

20131228-160001.jpg

20131228-162410.jpg

Building a TRAC bug display using Digispark

I decided to build a display for my desk at work that shows my current bug count using the 7 digit segment display that I purchased from DealExtreme. I do not want an Arduino hanging off a USB cable cluttering my desk (or draining my wallet). Enter the Digispark. Designed and produced by a local Portland company, the Digispark is an ATtiny85 that fits directly into a USB port.

On the computer side, A python program runs on my desktop. It queries Trac every once in a while and if the bug count changes, sends a message to the display.

IMG_0069

Code for the Digispark:

#include <DigiUSB.h>

#define latch 0
#define clock 1
#define data 2
#define kDigitCount 8

char gDisplayBuffer[kDigitCount+1];

void setup()
{
   pinMode(latch, OUTPUT);
   pinMode(clock, OUTPUT);
   pinMode(data, OUTPUT);
   
   memset(gDisplayBuffer, ' ', kDigitCount);
   writeToDisplay(true);
   
   DigiUSB.begin(); // open DigiUSB
}


void writeDigit(int digit, char inValue)
{
  byte value = 0;
  byte bitsToSend = 0;
  
  bitSet(bitsToSend, digit);
  
  digitalWrite(latch,LOW);
  shiftOut(data,clock,MSBFIRST, bitsToSend);
  
  
    switch (inValue)
    {
        case '.': value = B01111111; break;
        case '1': value = B11111001; break;
        case '2': value = B10100100; break;
        case '3': value = B10110000; break;
        case '4': value = B10011001; break;
        case '5': value = B10010010; break;
        case '6': value = B10000011; break;
        case '7': value = B11111000; break;
        case '8': value = B10000000; break;
        case '9': value = B10010000; break;
        case '0': value = B11000000; break;
        case 'H': value = B10001001; break;
        case 'i': value = B11111011; break;
        
        default:
        case ' ': value = B11111111; break;
    }
    
    shiftOut(data,clock,MSBFIRST,value);
  
  digitalWrite(latch,HIGH);
}

void writeToDisplay(bool inOveride)
{
  for (int i = 0 ; i < kDigitCount ; i++)
  {
    if (' ' != gDisplayBuffer[i] || inOveride)
      writeDigit(i, gDisplayBuffer[i]);
  }
}


int readBytesUntil(char delim, char* outBuffer, int inMaxSize)
{
  int bytesRead = 0;
  int lastRead = 0;
  
  while ( bytesRead < inMaxSize )
  {
    if (DigiUSB.available())
    {
      lastRead = DigiUSB.read();
  
      if ('\n' == lastRead)
        break;
        
      outBuffer[bytesRead] = lastRead;
      ++bytesRead;
    }
    
    // refresh the usb port for 10 milliseconds
    DigiUSB.delay(10);
  }
  
  outBuffer[bytesRead] = 0;
  
  return bytesRead;
}


void loop()
{
  char cmdBuffer[16];
  
    if (DigiUSB.available())
    {
      memset(cmdBuffer, ' ', 16);
      int iBytes = readBytesUntil('\n', cmdBuffer, 12);
      cmdBuffer[iBytes] = 0;
      
      if (!strncmp(cmdBuffer, "SET ", 4))
      {
        if (12 != strlen(cmdBuffer))
          DigiUSB.println("ERR");
        else
          memcpy(gDisplayBuffer, &cmdBuffer[4], kDigitCount);
      }
      else if (!strncmp(cmdBuffer, "CLR", 3))
      {
          memcpy(gDisplayBuffer, "        ", kDigitCount);
          
          writeToDisplay(true);
      }
    }
  
  writeToDisplay(false);
  DigiUSB.refresh();
}

The Python code:

'''
The Windows (or desktop) software that periodically gets the bug
count from TRAC and sends it to the 8 digit display.
'''

import urllib
import time

from arduino.usbdevice import ArduinoUsbDevice

kTracUrl = "http://trac/report?asc=1&format=csv"


################################################################################
class TracAggregation:
   def __init__(self):
      self.m_BugTickets = 0

   def incrementBugs(self):
      self.m_BugTickets += 1

   def totalBugs(self):
      return self.m_BugTickets
            
   def __str__(self):
      return str(self.m_BugTickets)
   

################################################################################
class TracEntry:
   def __init__(self):
      self.m_Ticket    = None
      self.m_Summary   = None
      self.m_Type      = None
      self.m_Priority  = None
      self.m_Milestone = None
   


################################################################################
def parseLine(inLine):    
   tokens = inLine.split(',')
   
   oEntry             = TracEntry()
   
   # change for your customized Trac query here
   oEntry.m_Ticket    = tokens[0]
   oEntry.m_Summary   = tokens[1]
   oEntry.m_Type      = tokens[4]
   oEntry.m_Priority  = tokens[5]
   oEntry.m_Milestone = tokens[6]
   
   return oEntry


################################################################################
def parseTracData(inData):
   
   entryList = []
   lineList  = inData.splitlines()
   
   for line in lineList[1:]: # skip the header
      
      try:
         oEntry = parseLine(line)
         entryList.append(oEntry)
      except:
         pass
      
   return entryList
   

################################################################################   
def aggregateTracData(parsedData, bExcludeBacklog=True):
   
   oData = TracAggregation()
   
   for oEntry in parsedData:
      if "Bug" == oEntry.m_Type:
         if bExcludeBacklog and "Backlog" != oEntry.m_Milestone:
            oData.incrementBugs()
         elif not bExcludeBacklog:
            oData.incrementBugs()
   
   return oData
   

################################################################################
def getTracData(url=kTracUrl):
   try:
      tracConnection = urllib.urlopen(url)
      tracData       = tracConnection.read()
      tracConnection.close()
   except Exception as e:
      print(e)
      return (False, None)
   
   parsedData     = parseTracData(tracData)
   aggregatedData = aggregateTracData(parsedData)
   
   return (True, aggregatedData)


################################################################################
def sendToDisplay(inMessage):

   try:
      theDevice = ArduinoUsbDevice(idVendor=0x16c0, idProduct=0x05df)
   except:
      return False

   for c in "SET ":
      theDevice.write(ord(c))
      
   for c in inMessage:
      theDevice.write(ord(c))
      
   return True



################################################################################
if __name__ == "__main__":
   
   lastTotalBugs = 0
   
   while (True):
      (ok, tracData) = getTracData()
      
      if ok and lastTotalBugs != tracData.totalBugs():
         displayMessage = str(tracData.totalBugs()).rjust(8, ' ')
         lastTotalBugs  = tracData.totalBugs()
         
         # only send the new bug count if it changed
         sendToDisplay(displayMessage)
      
      time.sleep(60)

Elenco AM radio kit

I purchased this Elenco – Two IC AM Radio Kit (Amazon Prime) as a small project to do with my son Isaiah (age nine). It does not require more than an hour or two of assembling. The PCB pads are nice and large, allowing a fair amount of slop and use of too much solder. The parts are easy to place and generally robust. The ferrite rod was dropped and cracked in several places; we repaired it with a bit of hot glue.

Isaiah_Elenco_AMRadio_Kit

Elenco_PCB

Isaiah_Elenco2

Enter the Dragino

dragino2

The Dragino v2 was just released. This little device runs OpenWRT, supports wifi and mesh networking, and has remote flash of AVR (or Arduino) devices. The power consumption is rated at 1 watt…which is really good. This makes it feasible to run this from solar power.

This device has dropped to a great price point: $45. This makes it equivalent to a raspberry pi. It’s a nice price point to do home automation or remote DAQ.

Edit 12/23: The Dragino is now starting to show up Amazon Prime. This sounds a lot of effort arranging shipping from China,

Finishing the pump controller

It’s been a busy summer. Next weekend I’m travelling down to the cabin. I keep telling myself it would be really nice to get the pump controller working. I spent some this weekend ironing out a few bugs–and creaking a mock to test it.

Mostly battling this stupid SSR. While the label says it takes 3VDC as control, I’ve found it takes at least 5 and better 6 volts to adequately turn on. At first I thought I burnt out the FloJet pump, but it was the SSR not fully triggering (only at a lower level).

Tonight I fixed the hysteresis feature (millis() is my friend). There is more work to do to finish out a feature features: (1) the manual overide switch (2) add a menu item to set the hysteresis time (3) test!