Skip navigation

Tag Archives: Arduino

2015-02-19 23.15.37

2015-02-19 23.14.37

I’ve been working on a project to build a WiFi controlled scoreboard. For a display prototype, I wanted to test out a multiplexing technique for the display. Multiplexing works well for a smaller display since it only requires 16 transistors to control the 9 digits X 7 segement, or 63 LEDs. The disadvantages of multiplex approach is that you have to constantly drive the display, and the reduced brightness due to the duty cycle. For a larger display, like what I may use for the final product, I think I will just directly control the segments directly instead of the multiplex approach.
I originally believed the multiplexed method would be better, due to the reduced number of power lines to the LEDs in comparison to directly driving each segment. However in both this design and a matrix driver I built up for the large display, I used shift registers to reduce it to a serial interface. Eventually what I realized after ordering this board is that I could use the shift registers chained together to create a programmable display that would hold its state. That, and I found an economical way to do the digits as PCBs instead of some type of 3d printed affair and adhesive LED strips. The non-PCB digits is why I thought I would have to run wires to each segment individually and join them into a 8 wire bus to the common drive switches. The PCB based square double digit boards reduce that to a 6 wire (GND, V logic, V LED, CLK, Data, Latch) board to board connection. I think in the larger design where inter-digit wiring will be needed, the cost of the extra transistors (which can have a lower power rating than the multiplex drives) will be more than offset by the cost and complexity of the extra wiring.

The design here is pretty simple, I’m just using 2 standard 74LS595 latching shift registers to feed data into the display. The latching feature is important, because in this application we don’t want the bits to just be shifting along the outputs. On the latch step, the internal 8 bit shifter is copied to the outputs all at once. There are 9 common anode 7 segment displays (I’m not using the dot), which works out to need exactly 16 bits. I’m using NPN transistors to both sink and source current to the display. See the schematic linked below. The code below is just some example code of feeding data to the display. You just have to clock the bits out to each of the shift registers and then latch the outputs. I didn’t chain the registers together, so there are 2 inputs to feed data two simultaneously, as the clock and latch pins are shared on the board. The code uses an array of bytes to map which bits to turn on for each number. One important note I had trouble with on the first attempt, data is clocked in on a low to high rising edge, but an additional high to low falling edge was required before latching, or else the final bit would never change. That doesn’t make much sense, so its possible it was just a timing issue and the extra port write gave the register enough time to settle before latching. The code was tested with the Arduino compiler using the VisualMicro Visual Studio Plugin.

Test Display Schematic

PCB

Shift Register Datasheet



/*
	|***A***|
	F	    B
	|***G***|
    E       C
	|***D***|

	D1 A-G, Com9
	D2 Com1 - Com8
	
*/
const int D1 = 2;
const int D2 = 3;
const int CLK = 5;
const int STROBE = 4;

byte digitMap[10] = 
{
	B11111100,
	B01100000, 
	B11011010,
	B11110010,
	B01100110,
	B10110110,
	B10111110,
	B11100000,
	B11111110,
	B11100110
};


int Score1 = 4;
int Score2 = 3;

int Min = 2;
int Sec = 0;

int Period = 2;

void setup()
{

  /* add setup code here */
	pinMode(D1, OUTPUT);
	pinMode(D2, OUTPUT);
	pinMode(CLK, OUTPUT);
	pinMode(STROBE, OUTPUT);

	digitalWrite(STROBE, LOW);
	digitalWrite(CLK, LOW);
	digitalWrite(D1, LOW);
	digitalWrite(D2, LOW);


}

int state = HIGH;
int count = 0;
void loop()
{

	for (int i = 0; i < 9; i++)
	{
		byte Data2 = B10000000 >> i;
		byte Data1 = getBits(i);
		if (i == 8) Data1 |= B00000001;

		//Write to display 
		for (int j = 0; j < 8; j++)
		{
			digitalWrite(CLK, LOW);
			digitalWrite(D1, Data1 & 0x01);
			digitalWrite(D2, Data2 & 0x01);
			Data1 = Data1 >> 1;
			Data2 = Data2 >> 1;
			digitalWrite(CLK, HIGH);
		}
		digitalWrite(CLK, LOW);
		digitalWrite(STROBE, HIGH);
		digitalWrite(STROBE, LOW);

	}
	count++;
	if (count > 0x0FF0)
	{
		if (Sec == 0)
		{
			Sec = 59;
			Min -= 1;
		}
		else
			Sec -= 1;
		count = 0;
	}


}

byte getBits(int digit)
{
	switch (digit)
	{
	case 0:
		return digitMap[Min / 10];
	case 1:
		return digitMap[Min % 10];
	case 2:
		return digitMap[Sec / 10];
	case 3:
		return digitMap[Sec % 10];
	case 4:
		return digitMap[Period];
	case 5:
		return digitMap[Score1 / 10];
	case 6:
		return digitMap[Score1 % 10];
	case 7:
		return digitMap[Score2 / 10];
	case 8:
		return digitMap[Score2 % 10];
	}

}

TempuratureSensor

Living in the great north, the winter cold always presents interesting challenges. A common fear is that your furnace will happen to give out while your away, then your pipes will freeze and burst, transforming your house into an indoor water park. Way back when, in the days of land line telephones, then made little devices that would watch the temp and give you a call if the temperature in your house fell too low. While I could have probably picked up something like that cheap, it wouldn’t have done much good since I don’t have a telephone line in my house. However, I saw it as a good opportunity to exercise my micro-controller skills with my a very simple internet based temperature sensor. While I had played with controllers before, I had never done much beyond blinking lights on a Of course, because I like to dive into things head first, I decided to layout my own custom board for the project. While I was at it, I though it would be a good time to learn SMD soldering as well.

The design is pretty simple, mostly just a combination of the Arduino board and an Ethernet shield. So an Atmega328 with a Wiznet 5100 Ethernet chip. For the temperature sensor, I went with the DS18B20. I had originally put a barrel jack, and the 3.3 and 5 volt regulators in the layout, but since I included the FTDI USB/Serial chip, that actually provides 5v and 3.3v rails from the USB. That way I could just use a cheap used micro USB phone charger for the power supply. I decided to put the Arduino headers in the layout as well, I’m not sure why since it just made routing the board more complicated, but I figured that way I could re-purpose it if needed. I think the rest of the passive parts I ordered from Digi-Key. If you are afraid of working with SMD parts like I was, just watch this, http://www.youtube.com/watch?v=b9FC9fAlfQE. Dave’s lesson is pretty good, and I was able to put my board together with just my 30W Radio Shack iron.

TempSense

Here is the Eagle files for the design. Two things about this design, first I’m not entirely sure about the regulator setup with the barrel jack. I built two boards, on the first one I populated those spots, and the rails seemed to be the right voltage, but the Ethernet never worked on that first board after I tried plugging a supply into the barrel jack. So I left those parts off the second board. Two, the board layout itself is terrible. I mean it works, and I’m no expert on board layout, but it just looks ugly to me looking back on this projects now. The one for sure issue I had was that I forgot to put the 4.7K pullup resistor on the sensors data pin (its missing from the linked files). That’s important, and it doesn’t work without it. However its was really easy just to drop it in between the pins on the back side of the board.

The coding for this was very simple. Usually, the biggest issue with devices like this, is how to access your data over the internet. Without a static IP address or dynamic DNS service, the best way to go is to send your data to a server. I decided to use a free service, Cosm (which is now Xively). The coding was done in the Arduino IDE and was literally just a combination of the DS18B20 example and the Cosm example code. Note in the below code I took out my API key and Feed number.

/**
 * Cosm Arduino sensor client example.
 *
 * This sketch demonstrates connecting an Arduino to Cosm (https://cosm.com),
 * using the new Arduino library to send and receive data.
 *
 * Requirements
 *   * Arduino with Ethernet shield or Arduino Ethernet (board must use the
 *     Wiznet Ethernet chipset)
 *   * Arduino software with version >= 1.0
 *   * An account at Cosm (https://cosm.com)
 *
 * Optional
 *   * An analog sensor connected to pin 2 (note we can still read a value from
 *     the pin without this)
 *
 * Created 8th January, 2013 using code written by Adrian McEwen with
 * modifications by Sam Mulube
 *
 * Full tutorial available here: https://cosm.com/docs/quickstart/arduino.html
 *
 * This code is in the public domain.
 */

#include <SPI.h>
#include <Ethernet.h>
#include <HttpClient.h>
#include <Cosm.h>
#include <OneWire.h> 


#define API_KEY "" // your Cosm API key
#define FEED_ID 0 // your Cosm feed ID


int DS18S20_Pin = 2; //DS18S20 Signal pin on digital 2

//Temperature chip i/o
OneWire ds(DS18S20_Pin); // on digital pin 2


// MAC address for your Ethernet shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

unsigned long lastConnectionTime = 0;                // last time we connected to Cosm
const unsigned long connectionInterval = 15000;      // delay between connecting to Cosm in milliseconds

// Initialize the Cosm library

// Define the string for our datastream ID
char sensorId[] = "home_temp";

CosmDatastream datastreams[] = {
  CosmDatastream(sensorId, strlen(sensorId), DATASTREAM_FLOAT),
};

// Wrap the datastream into a feed
CosmFeed feed(FEED_ID, datastreams, 1 /* number of datastreams */);

EthernetClient client;
CosmClient cosmclient(client);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

  Serial.println("Cosm Sensor Client Example");
  Serial.println("==========================");

  Serial.println("Initializing network");
  while (Ethernet.begin(mac) != 1) {
    Serial.println("Error getting IP address via DHCP, trying again...");
    delay(15000);
  }

  Serial.println("Network initialized");
  Serial.println();
}

void loop() {
  // main program loop
  if (millis() - lastConnectionTime > connectionInterval) {
    // read a value from the pin
    float sensorValue = getTemp();
    // send it to Cosm
    sendData(sensorValue);
    // read the datastream back from Cosm
    getData();
    // update connection time so we wait before connecting again
    lastConnectionTime = millis();
  }
}

// send the supplied value to Cosm, printing some debug information as we go
void sendData(float sensorValue) {
  datastreams[0].setFloat(sensorValue);

  Serial.print("Read sensor value ");
  Serial.println(datastreams[0].getFloat());

  Serial.println("Uploading to Cosm");
  int ret = cosmclient.put(feed, API_KEY);
  Serial.print("PUT return code: ");
  Serial.println(ret);

  Serial.println();
}

// get the value of the datastream from Cosm, printing out the value we received
void getData() {
  Serial.println("Reading data from Cosm");

  int ret = cosmclient.get(feed, API_KEY);
  Serial.print("GET return code: ");
  Serial.println(ret);

  if (ret > 0) {
    Serial.print("Datastream is: ");
    Serial.println(feed[0]);

    Serial.print("Sensor value is: ");
    Serial.println(feed[0].getFloat());
  }

  Serial.println();
}

float getTemp(){
 //returns the temperature from one DS18S20 in DEG Celsius

 byte data[12];
 byte addr[8];

 if ( !ds.search(addr)) {
   //no more sensors on chain, reset search
   ds.reset_search();
   return -1000;
 }

 if ( OneWire::crc8( addr, 7) != addr[7]) {
   Serial.println("CRC is not valid!");
   return -1000;
 }

 if ( addr[0] != 0x10 && addr[0] != 0x28) {
   Serial.print("Device is not recognized");
   return -1000;
 }

 ds.reset();
 ds.select(addr);
 ds.write(0x44,1); // start conversion, with parasite power on at the end

 byte present = ds.reset();
 ds.select(addr);  
 ds.write(0xBE); // Read Scratchpad

 
 for (int i = 0; i < 9; i++) { // we need 9 bytes
  data[i] = ds.read();
 }
 
 ds.reset_search();
 
 byte MSB = data[1];
 byte LSB = data[0];

 float tempRead = ((MSB << 8) | LSB); //using two's compliment
 float TemperatureSum = tempRead / 16;
 //convert to farenheit
 TemperatureSum = ((9.0/5.0) * TemperatureSum) + 32;
 return TemperatureSum;
 
}

And that’s pretty much it. I can watch my house temp wherever I am. https://xively.com/feeds/100416. Since Cosm changed to Xively, I’m not sure what their current pricing scheme is, but the public feed is still free on my current account.