< async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"">

Sunday, September 28, 2014

Controlling an led matrix using a single button using Morse code - a basic example for minimal communication channel

Morse code is a simple way to communicate using just two symbols. A short one and a long one. This can be helpful in many ways. 

Morse code 

Here is simple weekend project using an 8x8 led matrix and and an arduino uno (atmega 328). In this project, i used a single button to output any character or short messages from the led matrix.

This project is very simple and wiring is simple (see the sketch below). Connect an led to pin 13 (or just use the on-board led) and an input key (push button) to pin 7. A max7219 led matrix is connected to pin 8,9,10 on the arduino. 


led matrix with Morse control
It is basically using a nice library by raronoff which can encode and decode morse code. In addition, if you are using max7219 module, a maxmatrix library is also needed

See the video below for a short demo and the arduino code is given after that. The potential of this circuit is more than this. Input key can be replaced with various other inputs than a mere push button. It could be a delicate switch and couple of circuitry to track breath air, chest movements, eye lids movements etc



/*
  A simple single key control of max7219, 8 X 8 led matrix using morse code          
  Credits raronoff (morse encoder) : http://raronoff.wordpress.com/2010/12/16/morse-endecoder/
  Details at http://blog.riyas.org

WIRING DETAILS
=============

Arduino PIN 13 to an led (blue) via 330 ohm resister
Arduino pin 7 connected to push button swith, the other end is grounded via 330 ohm resistor
PIN 8 to  DIN pin of MAX7219 module
PIN 9 to CS pin of MAX7219 module
PIN 10 to  CLK pin of MAX7219 module
*/

#include <avr/pgmspace.h>
#include <MorseEnDecoder.h>
#include <MaxMatrix.h>


PROGMEM prog_uchar CH[] = {
3, 8, B00000000, B00000000, B00000000, B00000000, B00000000, // space
1, 8, B01011111, B00000000, B00000000, B00000000, B00000000, // !
3, 8, B00000011, B00000000, B00000011, B00000000, B00000000, // "
5, 8, B00010100, B00111110, B00010100, B00111110, B00010100, // #
4, 8, B00100100, B01101010, B00101011, B00010010, B00000000, // $
5, 8, B01100011, B00010011, B00001000, B01100100, B01100011, // %
5, 8, B00110110, B01001001, B01010110, B00100000, B01010000, // &
1, 8, B00000011, B00000000, B00000000, B00000000, B00000000, // '
3, 8, B00011100, B00100010, B01000001, B00000000, B00000000, // (
3, 8, B01000001, B00100010, B00011100, B00000000, B00000000, // )
5, 8, B00101000, B00011000, B00001110, B00011000, B00101000, // *
5, 8, B00001000, B00001000, B00111110, B00001000, B00001000, // +
2, 8, B10110000, B01110000, B00000000, B00000000, B00000000, // ,
4, 8, B00001000, B00001000, B00001000, B00001000, B00000000, // -
2, 8, B01100000, B01100000, B00000000, B00000000, B00000000, // .
4, 8, B01100000, B00011000, B00000110, B00000001, B00000000, // /
4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // 0
3, 8, B01000010, B01111111, B01000000, B00000000, B00000000, // 1
4, 8, B01100010, B01010001, B01001001, B01000110, B00000000, // 2
4, 8, B00100010, B01000001, B01001001, B00110110, B00000000, // 3
4, 8, B00011000, B00010100, B00010010, B01111111, B00000000, // 4
4, 8, B00100111, B01000101, B01000101, B00111001, B00000000, // 5
4, 8, B00111110, B01001001, B01001001, B00110000, B00000000, // 6
4, 8, B01100001, B00010001, B00001001, B00000111, B00000000, // 7
4, 8, B00110110, B01001001, B01001001, B00110110, B00000000, // 8
4, 8, B00000110, B01001001, B01001001, B00111110, B00000000, // 9
2, 8, B01010000, B00000000, B00000000, B00000000, B00000000, // :
2, 8, B10000000, B01010000, B00000000, B00000000, B00000000, // ;
3, 8, B00010000, B00101000, B01000100, B00000000, B00000000, // <
3, 8, B00010100, B00010100, B00010100, B00000000, B00000000, // =
3, 8, B01000100, B00101000, B00010000, B00000000, B00000000, // >
4, 8, B00000010, B01011001, B00001001, B00000110, B00000000, // ?
5, 8, B00111110, B01001001, B01010101, B01011101, B00001110, // @
4, 8, B01111110, B00010001, B00010001, B01111110, B00000000, // A
4, 8, B01111111, B01001001, B01001001, B00110110, B00000000, // B
4, 8, B00111110, B01000001, B01000001, B00100010, B00000000, // C
4, 8, B01111111, B01000001, B01000001, B00111110, B00000000, // D
4, 8, B01111111, B01001001, B01001001, B01000001, B00000000, // E
4, 8, B01111111, B00001001, B00001001, B00000001, B00000000, // F
4, 8, B00111110, B01000001, B01001001, B01111010, B00000000, // G
4, 8, B01111111, B00001000, B00001000, B01111111, B00000000, // H
3, 8, B01000001, B01111111, B01000001, B00000000, B00000000, // I
4, 8, B00110000, B01000000, B01000001, B00111111, B00000000, // J
4, 8, B01111111, B00001000, B00010100, B01100011, B00000000, // K
4, 8, B01111111, B01000000, B01000000, B01000000, B00000000, // L
5, 8, B01111111, B00000010, B00001100, B00000010, B01111111, // M
5, 8, B01111111, B00000100, B00001000, B00010000, B01111111, // N
4, 8, B00111110, B01000001, B01000001, B00111110, B00000000, // O
4, 8, B01111111, B00001001, B00001001, B00000110, B00000000, // P
4, 8, B00111110, B01000001, B01000001, B10111110, B00000000, // Q
4, 8, B01111111, B00001001, B00001001, B01110110, B00000000, // R
4, 8, B01000110, B01001001, B01001001, B00110010, B00000000, // S
5, 8, B00000001, B00000001, B01111111, B00000001, B00000001, // T
4, 8, B00111111, B01000000, B01000000, B00111111, B00000000, // U
5, 8, B00001111, B00110000, B01000000, B00110000, B00001111, // V
5, 8, B00111111, B01000000, B00111000, B01000000, B00111111, // W
5, 8, B01100011, B00010100, B00001000, B00010100, B01100011, // X
5, 8, B00000111, B00001000, B01110000, B00001000, B00000111, // Y
4, 8, B01100001, B01010001, B01001001, B01000111, B00000000, // Z
2, 8, B01111111, B01000001, B00000000, B00000000, B00000000, // [
4, 8, B00000001, B00000110, B00011000, B01100000, B00000000, // \ backslash
2, 8, B01000001, B01111111, B00000000, B00000000, B00000000, // ]
3, 8, B00000010, B00000001, B00000010, B00000000, B00000000, // hat
4, 8, B01000000, B01000000, B01000000, B01000000, B00000000, // _
2, 8, B00000001, B00000010, B00000000, B00000000, B00000000, // `
4, 8, B00100000, B01010100, B01010100, B01111000, B00000000, // a
4, 8, B01111111, B01000100, B01000100, B00111000, B00000000, // b
4, 8, B00111000, B01000100, B01000100, B00101000, B00000000, // c
4, 8, B00111000, B01000100, B01000100, B01111111, B00000000, // d
4, 8, B00111000, B01010100, B01010100, B00011000, B00000000, // e
3, 8, B00000100, B01111110, B00000101, B00000000, B00000000, // f
4, 8, B10011000, B10100100, B10100100, B01111000, B00000000, // g
4, 8, B01111111, B00000100, B00000100, B01111000, B00000000, // h
3, 8, B01000100, B01111101, B01000000, B00000000, B00000000, // i
4, 8, B01000000, B10000000, B10000100, B01111101, B00000000, // j
4, 8, B01111111, B00010000, B00101000, B01000100, B00000000, // k
3, 8, B01000001, B01111111, B01000000, B00000000, B00000000, // l
5, 8, B01111100, B00000100, B01111100, B00000100, B01111000, // m
4, 8, B01111100, B00000100, B00000100, B01111000, B00000000, // n
4, 8, B00111000, B01000100, B01000100, B00111000, B00000000, // o
4, 8, B11111100, B00100100, B00100100, B00011000, B00000000, // p
4, 8, B00011000, B00100100, B00100100, B11111100, B00000000, // q
4, 8, B01111100, B00001000, B00000100, B00000100, B00000000, // r
4, 8, B01001000, B01010100, B01010100, B00100100, B00000000, // s
3, 8, B00000100, B00111111, B01000100, B00000000, B00000000, // t
4, 8, B00111100, B01000000, B01000000, B01111100, B00000000, // u
5, 8, B00011100, B00100000, B01000000, B00100000, B00011100, // v
5, 8, B00111100, B01000000, B00111100, B01000000, B00111100, // w
5, 8, B01000100, B00101000, B00010000, B00101000, B01000100, // x
4, 8, B10011100, B10100000, B10100000, B01111100, B00000000, // y
3, 8, B01100100, B01010100, B01001100, B00000000, B00000000, // z
3, 8, B00001000, B00110110, B01000001, B00000000, B00000000, // {
1, 8, B01111111, B00000000, B00000000, B00000000, B00000000, // |
3, 8, B01000001, B00110110, B00001000, B00000000, B00000000, // }
4, 8, B00001000, B00000100, B00001000, B00000100, B00000000, // ~
};

int data = 8;    // DIN pin of MAX7219 module
int load = 9;    // CS pin of MAX7219 module
int clock = 10;  // CLK pin of MAX7219 module

int maxInUse = 1;    //change this variable to set how many MAX7219's you'll use

MaxMatrix m(data, load, clock, maxInUse); // define module

byte buffer[10]; //store morse temp

// Pin mappings
const byte morseInPin = 7;      
const byte morseOutPin = 13;

// Instantiate Morse objects
morseDecoder morseInput(morseInPin, MORSE_KEYER, MORSE_ACTIVE_LOW);
morseEncoder morseOutput(morseOutPin);

// Variables dealing with formatting the output somewhat
// by inserting CR's (carriage returns)
long lastTransmissionTime;
long currentTime;
boolean transmissionEnded = true; // Flag to mark old transmission is finished

// Minimum transmission pause time to insert carriage returns (CR)
// Adjust depending on Morse speed. IE 13 wpm = 646 ms between words (no CR).
const long transmissionPaused   = 1000; // Suitable for 13 wpm?


void setup()
{
  Serial.begin(9600);
  m.init(); // module initialize
  m.setIntensity(0); // dot matix intensity 0-15
  Serial.println("Led Control with morse");
  
  // Setting Morse speed in wpm - words per minute
  // If not set, 13 wpm is default anyway
  morseInput.setspeed(10);
  morseOutput.setspeed(10);
  
  lastTransmissionTime = (long)millis();
}



void loop()
{
  currentTime = (long)millis();
  
  // Needs to call these once per loop
  morseInput.decode();
  morseOutput.encode();

  // SEND MORSE (OUTPUT)
  // Encode and send text received from the serial port (serial monitor)
  if (Serial.available() && morseOutput.available())
  {
    // Get character from serial and send as Morse code
    char sendMorse = Serial.read();
    morseOutput.write(sendMorse);
    
    // Not strictly needed, but used to get morseSignalString before it is destroyed
    // (E.g. for morse training purposes)
    morseOutput.encode();

    // Also write sent character + Morse code to serial port/monitor
    Serial.write(' ');
    Serial.write(sendMorse);
    // Morse code in morseSignalString is now backwards
    for (int i=morseOutput.morseSignals; i>0; i--)
    {
      Serial.write(morseOutput.morseSignalString[i-1]);
    }
  }


  // RECEIVE MORSE (INPUT)
  // If a character is decoded from the input, write it to serial port
  if (morseInput.available())
  {
    // Get decoded Morse code character and write it to serial port/monitor
    char receivedMorse = morseInput.read();
    Serial.print(receivedMorse);
    printString(receivedMorse); // output the charecter to led matrix    
    // A little error checking    
    if (receivedMorse == '#') Serial.println("< ERROR:too many morse signals! >");
  }


  // Local Morse code feedback from input if not sending Morse simultaneously
  if (morseOutput.available()) digitalWrite(morseOutPin, morseInput.morseSignalState);


  // Check if ongoing transmission (not yet transmission pause)
  if (!morseOutput.available() || morseInput.morseSignalState == true)
  {
    // reset last transmission timer and flag
    lastTransmissionTime = currentTime;
    transmissionEnded = false;
  }

  // Format output with carriage returns after a transmission pause
  if ((currentTime - lastTransmissionTime) > transmissionPaused)
  {
    if (transmissionEnded == false)
    {
      // Separate the transmissions somewhat in the serial monitor with CR's
      for (int cr=0; cr<2; cr++) Serial.println("");  // some carriage returns..
      
      // Finally set the flag to prevent continous carriage returns
      transmissionEnded = true;
    }
  }
}

void printString(char s)
{
  int col = 0;
 
    if (s < 32) return;
    char c = s - 32;
    memcpy_P(buffer, CH + 7*c, 7);
    m.writeSprite(col, 0, buffer);
    m.setColumn(col + buffer[0], 0);
    col += buffer[0] + 1;

}

end

Saturday, September 20, 2014

A simple contact less human computer interface with ultrasonic transsducers and arduino uno or atmega328 [part 1]

Arduino ultrasonic computer interface- as there are
 only two sensors the active area is small
In this simple project, i used a pair of ultrasonic sensors (HC-SR04 modules) with arduino uno to check weather they can be used to track the x and y cordinates in a two dimensional plane. This is a beginner stage of a project where the number of transducers can be extended to produce a 3d mouse based on sonic sensors. This can be used to control simple things without really touching the computer so that it can be used in kitchen , labs etc when hands are dirty. Similar things can be achieved with infrared sensor and better results with a pair of video camera (but needs a better hardware than atmega328) The setup is very simple and is shown in the figure below. Two HC-SR04 sensors are hooked up in to arduino uno. Connection is straight forward (all pins are marked on the sensor silk screen). Connect the grounds to the ground pin of the uno/atmega328 and the positive (vcc) pins to 5v pins on the arduino. It uses the NewPing library which can be downloaded here. A simple explanation on connecting and using the library can be read at arduino playground

The whole set up in action can be seen in the video below. Basically the limitation in the active pointer area is small due to the limited number of sensors used. But this can be used as a base and adding more sensors can improve the results. Currently i used it to plot on a gui based on python (matplotlib) and can be extended as a real mouse by adding a few more lines of code and the V-USB on arduino uno or using a leonardo/ newer version of micro-controllers which can act as a usb HID.




The arduino sketch is very simple and it basically sends x and y cordinates via serial to the computer and on the PC , just plot the x,y values on a 2d plane

Code

sonic_mouse.ino

// a simple sonic mouse, code adapted from New ping example
// Connect PIN 13 to Trigger of first sensor and 12 to the Echo pin
// Connect PIN 11 to Trigger of second sensor and 10 to the Echo pin
// If you use multiple sensor for x / y axis, use the max value from the read array along the axis
// Read more about it at http://blog.riyas.org

#include <NewPing.h>

#define SONAR_NUM     2 // Number or sensors.
#define MAX_DISTANCE 50 // Maximum distance (in cm) to ping.
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

unsigned long pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.
unsigned int cm[SONAR_NUM];         // Where the ping distances are stored.
uint8_t currentSensor = 0;          // Keeps track of which sensor is active.

NewPing sonar[SONAR_NUM] = {     // Sensor object array.
  NewPing(13, 12, MAX_DISTANCE), // Each sensor's trigger pin, echo pin, and max distance to ping.
  NewPing(11, 10, MAX_DISTANCE),
};

void setup() {
  Serial.begin(115200);
  pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.
  for (uint8_t i = 1; i < SONAR_NUM; i++) // Set the starting time for each sensor.
    pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
}

void loop() {
  for (uint8_t i = 0; i < SONAR_NUM; i++) { 
    if (millis() >= pingTimer[i]) {         
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  
      if (i == 0 && currentSensor == SONAR_NUM - 1) SensorReadLoop(); 
      sonar[currentSensor].timer_stop();         
      currentSensor = i;                          
      cm[currentSensor] = 0;                      
      sonar[currentSensor].ping_timer(echoCheck); 
    }
  }
  // eXTRA  CONTROL CAN BE DONE HERE
}

void echoCheck() { 
  if (sonar[currentSensor].check_timer())
    cm[currentSensor] = sonar[currentSensor].ping_result;
}

void SensorReadLoop() { // ADDITIONALLY HERE you CAN ADD THE v-usb
  for (uint8_t i = 0; i < SONAR_NUM; i++) {
    Serial.print(cm[i]);
    Serial.print(" ");
  }
  Serial.print("\n");
}


Plotting the pointer data from arduino on the computer using python and matplotlib

Copy the following code in to a new file plot_mouse.py and execute it by typing python plot_mouse.py in a terminal. On windows, you need to modify it (first make sure to get the python serial and matplotlib working )

################################################################################
# plot_mouse.py
# Plot the data from sonic_mouse.ino on a pc
# It uses matplotlib
# install matplotlib, pyserial and numpy to get it working
# code adapted from https://gist.github.com/electronut/5641938
# more details on the project at http://blog.riyas.org
################################################################################

import sys, serial
import numpy as np
from time import sleep
from collections import deque
from matplotlib import pyplot as plt

# store the pointer data
class PointerData:
  def __init__(self, maxLen):
    self.ax = deque([0.0]*maxLen)
    self.ay = deque([0.0]*maxLen)
    self.maxLen = maxLen

  def addToBuf(self, buf, val):
    if len(buf) < self.maxLen:
      buf.append(val)
    else:
      buf.pop()
      buf.appendleft(val)

  def add(self, data):
    assert(len(data) == 2)
    self.addToBuf(self.ax, data[0])
    self.addToBuf(self.ay, data[1])  

class PlotData:
  def __init__(self, PointerData):
    plt.ion() 
    self.axline, = plt.plot(PointerData.ax,PointerData.ay,'r',linewidth=10)
    mng = plt.get_current_fig_manager()
    mng.resize(*mng.window.maxsize())    
    plt.ylim([0, 2000])
    plt.xlim([0, 2000])
  def update(self, PointerData):
    self.axline.set_xdata(PointerData.ax)
    self.axline.set_ydata(PointerData.ay)
    plt.draw()

# main() function
def main():
  if(len(sys.argv) != 2):
    print 'Example usage: python plot_mouse.py "/dev/ttyACM0"'
    exit(1)

  strPort = sys.argv[1];
  PointerData = PointerData(4)
  PlotData = PlotData(PointerData)
  # open serial port
  ser = serial.Serial(strPort, 115200)
  while True:
    try:
      line = ser.readline()
      #print line
      data = [float(val) for val in line.split()]
      if(len(data) == 2):
        PointerData.add(data)
        PlotData.update(PointerData)
    except KeyboardInterrupt:
      print 'exiting'
      break
  ser.flush()
  ser.close()
# call main
if __name__ == '__main__':
  main()

end

Thursday, September 11, 2014

Raspberry pi led matrix display showing scrolling weather forecast data - Simple led displays for Pi

Raspberry pi provide a simple solution for internet connected projects. Here i am going to use a simple led matrix display ( 8*8 with an option to daisy chain several of them) to display the weather forecast data from weather underground

Connecting an led matrix to the pi using a ribbon cable
led matrix is based on max7219, so the data can be send serially, saving gpio pins. It uses the spi pins on the raspberry pi and the entire matrix is controlled using a python script. Details on setting up the matrix can be read here. GPIO pin connections are described here. To install the libraries you may need to install python-development packages and build essentials (sudo apt-get install build-essential python-dev )

Then make sure that spi is enabled ( ls /dev/spi*) and should see spidev 0.0 and 0.1) , otherwise get the required modules. Recent versions of raspbmc has spi enabled by default, so no need to change anything.

To show the weather data, a python script is used which goes to weather underground and get the weather forecast for the current location. Signup for an api key and use it in the script  at "YOURapikeyHERE" (else it fails to work). Replace the zmw:00000.XXXXX.json with the value for your location ( just visit weather underground website and see the value for your place)

A cronjob can be used to automate the updates. An example script is shown below followed by a video

nano weather_update.py

To execute, sudo python weather_update.py

#!/usr/bin/env python
import urllib2
import json
import requests
import max7219.led as led
import max7219.canvas as canvas
import max7219.transitions as transitions
r = requests.get("http://api.wunderground.com/api/YOURapikeyHERE/forecast/q/zmw:00000.1.01257.json")
data = r.json()
day = data['forecast']['simpleforecast']['forecastday'] 
message1 = day[0]['date']['weekday']+' is '+day[0]['conditions']+' & temperature range is '+ day[0]['low']['celsius']+'-'+ day[0]['high']['celsius']
message2 = day[1]['date']['weekday']+' is '+day[1]['conditions']+' & temperature range is '+ day[1]['low']['celsius']+'-'+ day[1]['high']['celsius']
led.init()
led.brightness(1)
#show the weather report for some time , so loop 100 times
for x in range (100):
    # led.show_message(message1+"   " + message2, transition = transitions.left_scroll)
     led.show_message(message1+"   " + message2, transition = transitions.up_scroll)


 


And here is a demo

and more
Adding more led matrix display can make it easy to read

Saturday, September 6, 2014

Quick start with max7219 led dotmatrix display on raspberry pi and arduino - DIY led displays and name boards

Displays are an integral part of many DIY projects to convey information to the external word. Many micro-controller projects needs a simple way to communicate the information to the external world and we use led displays. It is possible to directly drive the led displays with a micro-controller, at the expense of the precious pins on the micros/ arduino. Added difficulties are the current control resistors and limitations on the display brightness etc. Here is a simple and popular solution for driving led displays using the MAX7219/MAX7221 a compact, serial input/output common-cathode display driver chips from maxim semiconductors. See Datasheet here

If you want to read more on direct drive solutions please see my posts here (clock using direct drive 7segment led ) or dotmatrix (external link) and pin connections and fonts

It needs only 3 pins from the microcontroller/arduino/raspberry pi to control the entire display. It is available in the form of low cost kits / can order the individual chip-set. Kits are pretty easy to assemle (see figure)

Assembly


MAX7219 kit components
Assembly is pretty simple if you just follow the silk screen. Keep the pcb with silk screen facing up and first solder the capacitors and resistors (take care of polarity on electrolytic capacitor). Solder the ic - socket (align the small wedge on the silk screen) and then all connectors.

The assembled led matrix is shown below

Assembled max 7219 led matrix (see the text on led array facing the bottom)

Quickly test the led matrix on arduino


I used the library Maxmatrix which can be downloaded here (unzip and copy the MaxMatrix folder to arduino/library folder. Load the example sketch to test the scrolling text pattern. (remember to set the number of led matrix if you use more than one and also the pin numbers). The connection is quite simple. I used

int data = 8;    // DIN pin of MAX7219 module

int load = 9;    // CS pin of MAX7219 module

int clock = 10;  // CLK pin of MAX7219 module


So change to what ever pin you intend to use and also if you use more matrices , connect DOUT to the next matrix and the rest are parallel.


Usage on the Raspberry pi -> i followed the guide here