I’ve been meaning for a while now to cycle back and work on the SSMU project again. The Hackaday Prize entry submission deadline was quickly approaching so I decided to put something together for motivation. Most of the details will be tracked over on their projects site: http://hackaday.io/project/2424-Simulation-Source-Measurement-Unit

The idea is still the same – integrate core functions from lab equipment over the audio frequency range and integrate with software running a circuit simulator. Instead of sticking to through hole components and an MSP430, I’m going to use a STM32L100RBT6 and a custom PCB. The current planned features are the following:

1-2* voltage sources (0-15V, 1MSps DAC)
1 current source (up to about 50mA)
2+ voltage measurements (12bits, 1MSPS ADC)
2 current measurements
2 Bode plot measurements (gain and phase up to 100kHz)
Fixed and adjustable power supply (3.3V, 5V, 0-9V)
Adjustable current limits for 0-9V rail and DAC output
Protocol analyzer (at least I2C, SPI, and UART)
LCR meter
Speaker output stage
USB and wireless (nRF24L01+) connectivity

*One voltage source is used for the current source but will be accessible when the current source is not in use

I’ve picked most of the components except for some current and voltage protection chips. The BOM is sitting somewhere around $20-25 so I’m on track to not break the bank.

Instead of measuring magnitude and phase by brute force sampling and software, I’m going to extract it with a few op amps. In the picture below, U1 attenuates the voltage at the DUT (can range up to 9-15V) and optionally provides amplification for lower amplitude signals. C2 compensates for some of the input capacitance the DUT might present. U2 and D1 form a precision rectifier and along with C1 constitute a peak detector. This relatively stable voltage can then be sampled by the microcontroller’s ADC as the magnitude of the waveform.

This value is then divided and compared with the input waveform again to produce a digital output representing when the waveform is above or below this fixed voltage. When compared with the digital output for the other Bode circuit (one at either side of the DUT) the microcontroller can determine the phase shift.

bode

wifiscreen_demo

I recently bought a Intel® Centrino® Advanced-N 6235 Mini PCI card to enable WiFi communication for my Intel Galileo board. Since I needed to add wireless display capabilities to my SSMU project, I decided to do a mini project to become familiar with sending and receiving data over WiFi.

The libGDX app launches and waits for the user to enter the IP address and port number the client (phone/tablet/computer) should use to connect to the server (Galileo). Once connected, the Galileo can send commands to draw shapes, text, and images based on the Arduino TFTLibrary API.

To save communication bandwidth, the Galileo only sends new images to the client which then caches the image. This way subsequent draw commands for that image only require the client to fetch the image from its local storage.

The python class and associated executables can be downloaded from my GitHub account along with the current list of known issues and planned features. I will be releasing the full source of the libGDX app shortly.

The video below shows Galileo simultaneously drawing the above demo image to four devices (phone, tablet, laptop, and desktop). I believe the lag for the tablet was due to an application trying to update itself since the Galileo had completed sending commands.

http://www.youtube.com/watch?v=L-v12MY934k

Image

This project started out as a way to see how much useful work I could get out of a MSP430G2553. Since the closest thing I have to a personal electronics lab is a National Instruments myDAQ from college, I decided to try and replicate some of its functionality (namely the ability to act as a SMU) as well as integrating with a circuit simulator. The project goals were the following (in no particular order):

  • Low cost (< $100)
  • Through hole components when possible/feasible
  • At least 200ksps ADC
  • Sample window of at least 0.15 seconds (enough for 3 cycles of a 20Hz wave)
  • Sweepable sine wave output (20Hz – 20kHz)
  • Source and measure current (specific specs TBD)
  • Integration with Intel Galileo board running ngSpice
  • GUI on desktop and Android
  • Support both wired (USB) and wireless (WiFi) connections from Galileo

Currently I have a desktop GUI (written in Java with the libGDX framework) connected over USB to the Galileo. Based on what commands the GUI sends, the Galileo will either run a ngSpice simulation file or request new data from the MSP430 ADC. The results will be written to a file and sent back to the GUI. After some file parsing to determine how many signals were in the file and the number of samples, the GUI updates the plot as shown below.

Voltage in (red), voltage out (white) ngSpice signals plus ADC (green).

Voltage in (red), voltage out (white) ngSpice signals plus ADC (green).

Here the simulated circuit was a resistor divider (factor of 2) with a 7.1kHz sine wave input. When new ADC data is requested, the MSP430 starts stepping through a look-up table and outputs the value it reads to the DAC (TLV5618). The internal ADC is then configured to start a block of conversions and to use the DMA channel. This way results are directly copied to memory with low jitter (1-3 MCLK, or 0.0625us – 0.1875us for the 16MHz clock) and without interrupting the CPU.

In the above picture, the DAC was outputting a sine wave with 16 steps per period at about 7.1kHz. The green data points are from the ADC which was running at 62.5ksps.

Currently, the fastest sine wave the MSP430 can synthesize is 7.5kHz with 16 steps per period. I might be able to double this by using a DAC that only requires a single byte to update its output instead of two bytes like the TLV5618. I included a picture of the myDAQ’s oscilloscope reading below. It doesn’t quite agree with 7.1kHz but I’ve seen it jump a few hundred hertz based on what the timescale is.

myDaq_7100

Since there’s 512 bytes of RAM, only 30 10-bit ADC results can be stored at a time before I start to see stack overflows. Even though 8-bits would provide acceptable data, the ADC and its DMA channel always copy both bytes for each conversion.

My next steps for the project are the following:

  • Add an external SPI RAM chip to buffer more samples. The Galileo and MSP430 will trade off reading and writing to it.
  • Determine the specs required for current measurement and order the appropriate parts.
  • Provide more signal conditioning so voltages aren’t limited to 0-3.3V.
  • Add an interface to the GUI for editing ngSpice files. Currently I have a telnet window open to make changes.
  • Test GUI on Android which will mean sending the data over WiFi.
  • Test out a clock-tunable RC low-pass filter + VCO IC for lower distortion sine waves over the entire 20Hz-20kHz range.

In this demo, the Galileo communicates with a MSP430G2553 microcontroller via the nRF24L01+ 2.4GHz transceiver. The MSP430 sends an encrypted block of data representing the state of six of its pins. This message is received by the sketch running on the Galileo and stored in shared memory. A Python script then wakes up, decrypts the message, and prints which keys were pressed to the screen. The purpose of this demo is to show how a simple sketch can take advantage of a different user space program on the Galileo to do something as complex as decryption.

Edit: There is now an Instructable version here. The updated code is now hosted on my GitHub – https://github.com/bunneydude/IPCBuffer (support for multiple readers/writers, larger buffers, full source code).

boards

flowcharts

MSP430

There are 6 pins on the MSP430 configured as inputs with their internal pull-up resistor connected to act as keys for the user to press. When a key is pressed (pin grounded by the long red wire shown wrapped around a pencil) or released (pin is pulled high by internal resistor) the MSP430 reads the state of all 6 pins, encrypts with AES-128 in ECB mode, and transmits to the Galileo. The board accepts up to 26V for input power and regulates it down to 3.3V with the LM2937. The nRF24L01+ (found on eBay for as low as $1 per board) draws about 26uA during standby, 11.3mA during transmit, and 13.5mA during receive (these values can be lowered by reducing the chip’s antenna output power and/or data rate). The radio is set to transmit at 2Mbps but can be programmed to 1Mbps or 250kbs to extend the range of the transmission. So far I’ve only tested indoors with direct line of sight up to about 15 feet. The AES code used comes from Texas Instruments. They make no promises about being safe from side channel attacks, but for now all I need is a correct implementation that will fit in the MSP430G2553’s limited memory. The library used to communicate with the nRF24L01+ comes from here with only a few modifications needed to port the code to the MSP430 and Galileo.

Sketch

The sketch running on the Galileo runs code similar to the MSP430 except that it handles receives from the nRF24L01+ instead of transmits. In the setup() method, the sketch creates three IPC objects: a shared memory segment one AES block length in size (16 bytes) and two semaphores. Together these form the control needed to make a queue with a single producer (sketch) and single consumer (Python script). Semaphore sem_space is initially 1 and represents how much space is available in shared memory for new encrypted messages. After receiving a new message, the sketch attempts to decrement the count of sem_space. The sketch will idle if sem_space is already 0 (indicating that the Python script hasn’t read the previous message out of shared memory). After storing the encrypted message in shared memory, the sketch increments sem_data (initially 0) to signal to the Python script that new data is available. You can enter ‘cat /proc/sysvipc/shm’ or ‘cat /proc/sysvipc/sem’ in the command prompt to see the current list of shared memory and semaphore objects, respectively.

Defining IPC object values (before setup())

#define SHM_SIZE 16 // bytes of shared memory to create
#define SHM_QUEUE_LENGTH 1 //number of AES blocks that will fit
int shmid; // shared memory for received encrypted data
int sid_data; // data in queue ready for decryption
int sid_space; // room in queue
// hard-coded keys
key_t key_shmid = 123456;
key_t key_sid_data = 654321;
key_t key_sid_space = 987654;    
uint8_t* shared_memory;

struct sembuf sem_down = {0, -1, 0};
struct sembuf sem_up = {0, 1, 0};

Setup IPC objects with calls to galileo_ipc library

// temp contains the error code: 0 on success, 1 or 2 on error
temp = startMem(&shmid, key_shmid, &shared_memory, SHM_SIZE);
temp = startSem(&sid_data, key_sid_data, 0);
temp = startSem(&sid_space, key_sid_space, SHM_QUEUE_LENGTH);

Writing to shared memory

if(nrf24_dataReady()){      
    nrf24_getData(rx_data_array);         
    semop(sid_space, &sem_down, 1); // -1 to space in queue
    memcpy(shared_memory, rx_data_array, 16);       
    semop(sid_data, &sem_up, 1);  // +1 to data in queue
}

The galileo_ipc library (WIP)

#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include "galileo_ipc.h" // currently only contains function prototypes

union semun{ 
  int val;
  struct semid_ds *buf;
  ushort *array;
  struct seminfo *__buf;
  void *__pad;
};

union semun semopts; 

uint8_t startMem(int* shmid, key_t key, uint8_t** mem, uint8_t size){
  // Setup shared memory
  if ((*shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666)) < 0){
     return 1;
  }

  // Attach shared memory
  if ((*mem = (uint8_t *)shmat(*shmid, NULL, 0)) == (uint8_t *)-1){
     return 2;
  }
  return 0;
}

uint8_t startSem(int* sid, key_t key, uint8_t val){
  // Setup semaphore
  if ((*sid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0660)) < 0){
   return 1;
  }

  // Initialize count
  semopts.val = val;
  if ((semctl(*sid, 0, SETVAL, semopts)) < 0){
   return 2;
  }
  return 0;
}

Python

The Python script accesses the three IPC objects using their hard-coded key values (see the Limitations/Future Improvements section for details on this behavior). It then tries to decrement sem_data, going idle if the sketch has not placed any new encrypted message in shared memory. When the decrement to sem_data succeeds, the message is read from shared memory and sem_space is incremented so the sketch can place a new message in shared memory. In the code below, the strings “one”, “two”, etc are just large strings from an ASCII art generator. I’ve omitted them from this post to save space. I cross-compiled the PyCrypto and sysv_ipc Python modules from an Ubuntu virtual machine on my Windows 8 desktop.

from Crypto.Cipher import AES
import sysv_ipc
import os
numbers = {'1':one,'2':two,'3':three,'4':four,'5':five,'6':six,'0':zero}

keys = {'0x3b':1,'0x3d':2,'0x3e':3,'0x37':4,'0x2f':5,'0x1f':6,'0x3f':0}

key_shm = 123456
key_sem_data = 654321
key_sem_space = 987654

key = "000102030405060708090a0b0c0d0e0f".decode('hex')
obj = AES.new(key,AES.MODE_ECB)

# get ipc objects created by sketch
mem = sysv_ipc.SharedMemory(key_shm)
sem_data = sysv_ipc.Semaphore(key_sem_data) # initially 0
sem_space = sysv_ipc.Semaphore(key_sem_space) # initially 1

last_key = 0;
current_key = 0;

while(1):
    sem_data.acquire() #wait here until encrypted data is ready
    val = mem.read()  
    sem_space.release() #indicate data has been read from shm
    msg = obj.decrypt(val) 
    #print(map(hex,map(ord,msg))) #print decrypted data

    current_key = keys[hex(ord(msg[0]))]
    os.system('clear')
    if(current_key == 0):
        print("Key " + str(last_key) + " released.\n")
    else:
        print("Key " + str(current_key) + " pressed.\n")
        print(numbers[str(current_key)])
    last_key = current_key

Demo Video

I noticed the majority of the delay here was due to printing those large ASCII art numbers (connected over Ethernet to Galileo). Without the extra printing, the message of which key was pressed was nearly instantaneous (from the timescale of a human of course). I apologize for how dark the Galileo board looks – I brought a lamp over for more light but my phone still didn’t adjust fast enough.

Limitations/Future Improvements

While the MSP430 transmits the state of all 6 pins (i.e. multiple keys could be pressed at the same time) only single key presses are demonstrated in the video. It was annoying enough to keep both board and screen in frame while holding the camera and pressing different keys (eventually I’ll set up some sort of small tripod to make future videos easier).

As mentioned above, using ECB mode for AES isn’t the best idea in the world. Granted, the point of this demo was not to demonstrate a complete security solution (that would require a key exchange protocol, better AES mode, etc). Even then there would most likely be side channel attacks on both PyCrypto and whatever code is on the MSP430.

Instead of hard-coding keys for the IPC objects (done here for simplicity) you could use the ftok(3) function. However, there’s still the possibility for there to be a key collision. The sketch needs a reasonable way to respond to such an error so that the Python script doesn’t try to access an IPC object that doesn’t exist. The IPC_EXCL flag is OR’d in for shmget() and semget() to make sure you don’t end up opening an object created by a different program. I noticed that the IPC objects would be cleaned up if the sketch was killed but not if you re-programed the sketch. The best practice is to clean up the IPC objects yourself (I added a serial command that results in calls to shmdt() and shmctl() to take care of this). If you leave off the IPC_EXCL flag for this demo, the sketch will still acquire the old IPC objects and run properly since it re-initializes the semaphores. However, there might still exist a race condition with the Python script if you don’t kill it before re-programming the sketch.

Data only flows from the sketch to the Python script here due to the nature of the data. It would be trivial to copy and modify the above code to set up another shared memory producer-consumer queue. This would be useful in a project where the sketch needs to use the decrypted data to transmit a new message over the radio. Increasing SHM_QUEUE_LENGTH and SHM_SIZE appropriately would allow for the sketch to load more than one message for the Python script to decrypt.

The galileo_ipc library will be formatted so that conforms to the built-in Arduino libraries. That way its functions will be in a familiar form (something like IPC.createShm() with syntax highlighting).  I’ll need IPC in my next project so I will be updating galileo_ipc along the way (switching from SysV to POSIX-style IPC objects/functions for starters).

Image

Since I wanted more fine grade copper powder but didn’t want to deal with the cost/quantity of ordering online I decided to make my own. The idea is fairly simple: dissolve a chunk of copper then introduce a source of iron. The iron will knock the copper out of solution and you’ll end up with a nice copper powder. In theory, of course.

In practice this ended up being somewhat of a pain. Partly because I don’t have access to a real chemistry lab so I instead did the work sitting outside on concrete.

copper_etching_zoom

In the above picture, the container has a piece of copper pipe (roughly 0.75″ by 0.75″) and ferric chloride. The container in the picture below has an earlier solution of ferric chloride that had already reacted with the copper. You can see small pieces of copper at the bottom that were once a part of the steel wool.

copper_precipitate

Trying to drain off the excess liquid of the second container to get at the copper was more annoying than I expected. I didn’t have any good substance handy to wash off the residue so most of the copper stuck together in a black mess. Unfortunately it seems I didn’t take pictures of what it looked like in the end, but just imagine small copper-colored pieces of steel wool in a clump.

It’s worth noting that the copper didn’t just precipitate to uniform particles. It replaced the top layers of the steel wool – which makes sense because it is the iron after all which is knocking the copper out of the solution. Thicker pieces of steel wool had enough iron left over that a magnet could still drag them around whereas the finer grade steel wool became more like copper and less like iron (no response to the magnet).

Follow

Get every new post delivered to your Inbox.