Project: Display periodic web-based data on an LED display using Java
I needed a relatively quick end-to-end solution where I could display web based data on an external circuit.
The circuit consists of 4 lines of 8 (7-segment LED displays). The display as configured is multiplexed by standard 74HC595 SIPO shift registers - 4 sets of 2 in total for independent column and data line drivers. The microprocessor does not drive the displays directly - it only drives the shift|load|data lines of the 4 sets of shift registers.
The modules of the project are as follows...
- LED display multiplexer (4 lines)
- Microcontroller COMM port tranceiver
- Microcontroller serial to display logic
- PC COMM port tranceiver
- PC HTTP receiver
- PC HTML parser
The last 3 modules (hardware comms, http comms and html parser) can all be run from a single SE Java application on the host PC where the device is connected to a USB serial port.
Third Party Software Plugins:
- eclipselink.jar - EclipseLink 2.1.2 ORM implementation of JPA 2.0 API (part of JEE6)
- javax.persistence_2.0.3.v*.jar - The JPA 2.0 API specification interfaces jar
- derbyclient.jar - Derby Java Database 10.5.3.0 client jar
- comm.jar - The SUN Java Communications API jar (for serial/USB port comms)
BOM (Bill of Materials):
- 1 - 40 pin DIP Parallax Propeller PAX32 8-core 32-bit Microcontroller running ASM or SPIN bytecode (with integrated 5MHz crystal, 32Kb EEPROM and FTDI USB controller/jack)
- 8 - 16 pin 74HC595N DIP SIPO shift registers with separate shift/load registers from Parallax, Digikey.ca or futureelectronics.ca
- 16 - blue 7-segment SURE electronics LED digit display
- 16 - red 7-segment SURE electronics LED digit display
- 5 - 63 row breadboards
- 1 - 300-1000mF electrolytic power capacitor
- 1 - 7803 3.3v power transistor with head sink (we get up to 45(c))
- 1 - 7.5 - 9.0v 1A power supply (USB cannot power the board)
- n - 0.1 inch (breadboard compatible) discrete LED from evilmadscience.com - an excellent electronics components distributor
Java code:org.eclipse.persistence.jpa.PersistenceProvider org.dataparallel.device.model.BugNumber
Javaagent resource
Place the following file in your /src/META-INF/services/javax.persistence.spi.PersistenceProvider file.
org.eclipse.persistence.jpa.PersistenceProvider
SerialDriver.java
package org.dataparallel.device.driver; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.GregorianCalendar; import java.util.TooManyListenersException; // Sun's serial port driver import javax.comm.CommPortIdentifier; import javax.comm.PortInUseException; import javax.comm.SerialPort; import javax.comm.SerialPortEvent; import javax.comm.SerialPortEventListener; import javax.comm.UnsupportedCommOperationException; /** * This code will writes to a full duplex serial port (a USB FTDI controller) that * is connected to a Parallax Propeller 8-core microcontroller that runs a 4 line 32 digit display. * See * http://www.dataparallel.com * * References: * Sun Java Communication API * * See the following tutorial by Rick Proctor for the Lego RCX Brick at * http://dn.codegear.com/article/31915 * Prerequisites: * Java Serial Support on Windows * See "PC Serial Solution" for the Lego(TM) RCX Brick at the following URL at MIT * - it explains how to get the SUN javax.comm API working on Windows (it is officially supported only on Solaris and Linux). * http://llk.media.mit.edu/projects/cricket/doc/serial.shtml * * SUN/Oracle Java Communications API 3.0 (1998-2004) * http://www.oracle.com/technetwork/java/index-jsp-141752.html * - copy comm.jar and javax.comm.properties to both yourJDK and JRE lib directories * - copy win32com.dll to both your JDK and JRE bin directories - no need for a registration via regsvr32 * - in your IDE (IE: eclipse.org) project add a library reference to comm.jar to get your javax.comm java code to compile * * 20101025 : bi-directional command format 32 : 96 + address=0..31 : 48 + value=0..11 --> IE: put(0,9) --> 32:48:105 * Note: comm start will reset the Propeller chip - therefore make sure SPIN or ASM * code is written to EEPROM and not just RAM. * */ public class SerialDriver implements SerialPortEventListener, Runnable { // http://download.oracle.com/docs/cd/E17802_01/products/products/javacomm/reference/api/javax/comm/CommPortIdentifier.html private static CommPortIdentifier portId; private InputStream inputStream; private OutputStream outputStream; private SerialPort serialPort; private Thread readThread; private int baud = 0; private String commPortName; private int[] propellerLoadMessage = {10,10,2,6,6,9,1,2, 10,3,1,6,5,1,3,10, 10,2,2,4,1,9,2,10, 3,1,4,5,1,9,10,10}; // Display Device specific details public static final String DEFAULT_COMM_PORT_NAME = "COM15"; public static final int DEFAULT_COMM_PORT_BAUD = 38400; public static final int NUMBER_DISPLAY_DIGITS = 32; public static final int NUMBER_DISPLAY_LINES = 4; public static final int NUMBER_DISPLAY_DIGITS_PER_LINE = 8; public static final int CHAR_CODE_BLANKING = 10; public static final int CHAR_CODE_DEC_POINT_ONLY = 11; private static final int PROTOCOL_COMMAND_CODE = 32; private static final int PROTOCOL_COMM_DELAY_MS = 80; private static final int PROTOCOL_INDEX_CHAR_OFFSET = 96; public SerialDriver() { this(DEFAULT_COMM_PORT_NAME, DEFAULT_COMM_PORT_BAUD); } public SerialDriver(String portOverride) { this(portOverride, DEFAULT_COMM_PORT_BAUD); } public SerialDriver(String portOverride, int baudOverride) { if(baudOverride > 0 && baud == 0) { baud = baudOverride; } if(null == portOverride) { portOverride = DEFAULT_COMM_PORT_NAME; } boolean validPort = initialize(portOverride); if(validPort) { try { // Open appName=SerialDriver with timeout=2000 ms serialPort = (SerialPort) portId.open("SerialDriver", 2000); System.out.println(getTimeStamp() + ": " + portId.getName() + " opened for Propeller chip communications"); } catch (PortInUseException e) { e.printStackTrace(); } if(null != serialPort) { // Get an input stream on the loader propeller try { inputStream = serialPort.getInputStream(); } catch (IOException e) { e.printStackTrace(); } // Add this class as the listener on the loader port try { serialPort.addEventListener(this); } catch (TooManyListenersException e) { e.printStackTrace(); } // notify the loader port that we wish to capture events using this class serialPort.notifyOnDataAvailable(true); try { // setup the loader port serialPort.setSerialPortParams(baud, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); serialPort.setDTR(false); serialPort.setRTS(false); } catch (UnsupportedCommOperationException e) { e.printStackTrace(); } readThread = new Thread(this); readThread.start(); } } else { System.out.println("Unable to open port: " + portOverride); } } private boolean initialize(String portName) { commPortName = portName; try { portId = CommPortIdentifier.getPortIdentifier(commPortName); } catch (Exception e) { System.out.println(getTimeStamp() + ": " + commPortName + " " + portId); System.out.println(getTimeStamp() + ": " + e); e.printStackTrace(); } if(null == portId) { return false; } else { return true; } } public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } private static String getTimeStamp() { GregorianCalendar aDate = new GregorianCalendar(); StringBuffer time = new StringBuffer(); time.append(aDate.get(GregorianCalendar.YEAR)); time.append("."); time.append(aDate.get(GregorianCalendar.DAY_OF_MONTH)); time.append("."); time.append(aDate.get(GregorianCalendar.MONTH) + 1); time.append("_"); time.append(aDate.get(GregorianCalendar.HOUR_OF_DAY)); time.append(":"); time.append(aDate.get(GregorianCalendar.MINUTE)); time.append(":"); time.append(aDate.get(GregorianCalendar.SECOND)); time.append("."); time.append(aDate.get(GregorianCalendar.MILLISECOND)); return time.toString(); } public void setIndex(int index, int value) { propellerLoadMessage[index] = value; } public int getBaud() { return baud; } public String getCommPortName() { return commPortName; } private String getIndexValuePair() { StringBuffer buffer = new StringBuffer(); int value, index; try { // get index value = inputStream.read(); if(value == PROTOCOL_COMMAND_CODE) { value = inputStream.read(); } buffer.append(Integer.toString(value)); buffer.append("@"); // get value index = inputStream.read(); buffer.append(Integer.toString(index - PROTOCOL_INDEX_CHAR_OFFSET)); } catch (IOException e) { e.printStackTrace(); } finally { try { outputStream.flush(); outputStream.close(); } catch (IOException e2) {}// ignore exception on close } return buffer.toString(); } public void serialEvent(SerialPortEvent event) { switch (event.getEventType()) { case SerialPortEvent.BI: case SerialPortEvent.OE: case SerialPortEvent.FE: case SerialPortEvent.PE: case SerialPortEvent.CD: case SerialPortEvent.CTS: case SerialPortEvent.DSR: case SerialPortEvent.RI: case SerialPortEvent.OUTPUT_BUFFER_EMPTY: break; case SerialPortEvent.DATA_AVAILABLE: StringBuffer readBuffer = new StringBuffer(); int c; try { while ((c = inputStream.read()) != 13) { // look for command if (c == PROTOCOL_COMMAND_CODE) { readBuffer.append(getIndexValuePair()); System.out.println("IN: " + readBuffer.toString()); readBuffer = new StringBuffer(); } } inputStream.close(); } catch (IOException e) { e.printStackTrace(); } finally { try { outputStream.flush(); outputStream.close(); } catch (IOException e2) {}// ignore exception on close } break; } } public void push(int index, int value) { push(index, value, DEFAULT_COMM_PORT_NAME); } public void push(int index, int value, String portOverride) { if(null != serialPort) { try { outputStream = serialPort.getOutputStream(); // protocol dictates that we write each byte twice // prefix command outputStream.write(PROTOCOL_COMMAND_CODE); outputStream.write(PROTOCOL_COMMAND_CODE); // index 0-23 = 96-119 outputStream.write(index + PROTOCOL_INDEX_CHAR_OFFSET); // value 48-57 outputStream.write(value); // terminate command outputStream.write(13); System.out.println("OUT: " + value + "@" + (index)); Thread.sleep(PROTOCOL_COMM_DELAY_MS); outputStream.flush(); outputStream.close(); } catch (IOException ioe) { ioe.printStackTrace(); } catch (InterruptedException ie) { ie.printStackTrace(); } } } public void drive() { for(;;) { // push out 3 to get the stream started (first 2 are skipped) push(0, propellerLoadMessage[0]); push(0, propellerLoadMessage[0]); push(0, propellerLoadMessage[0]); for (int i=0;i<NUMBER_DISPLAY_DIGITS;i++) { push(i, propellerLoadMessage[i]); } } }
BugNumber.java
package org.dataparallel.device.model; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import javax.persistence.Version; @Entity @Table(name="DEVICE_BUGNUMBER") public class BugNumber implements Serializable { private static final long serialVersionUID = 3132063814489287035L; @Id // keep the sequence column name under 30 chars to avoid an ORA-00972 @SequenceGenerator(name="DEV_SEQUENCE_BUGN", sequenceName="DEV_BUGN_SEQ", allocationSize=15) @GeneratedValue(generator="DEV_SEQUENCE_BUGN") @Column(name="BUG_ID") private Long id; @Version @Column(name="BUG_VERSION") private int version; @Basic private long number; private int priority; private boolean assignedStatus; private String assignedTo; public BugNumber() { } public BugNumber(String aNumber) { number = Integer.parseInt(aNumber); } public char[] getCharDigits() { return Long.toString(number).toCharArray(); } public List getIntDigits() { List digits = new ArrayList(); for(Character aChar : getCharDigits()) { digits.add(new Integer(Character.getNumericValue(aChar.charValue()))); } return digits; } public long getNumber() { return number; } public void setNumber(long number) { this.number = number; } public int getPriority() { return priority; } public void setPriority(int priority) { this.priority = priority; } public boolean isAssignedStatus() { return assignedStatus; } public void setAssignedStatus(boolean assignedStatus) { this.assignedStatus = assignedStatus; } public String getAssignedTo() { return assignedTo; } public void setAssignedTo(String assignedTo) { this.assignedTo = assignedTo; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } public static void main(String[] args) { BugNumber aBug = new BugNumber("266912"); aBug.getCharDigits(); aBug.getIntDigits(); System.out.println(aBug.getNumber()); } }DisplayConsoleClient.java
package org.dataparallel.device.driver; public class DisplayConsoleClient { /** * @param args */ public static void main(String[] args) { // get port if passed in String portName = SerialDriver.DEFAULT_COMM_PORT_NAME; int baud = SerialDriver.DEFAULT_COMM_PORT_BAUD; if(null != args) { if(args.length > 1) { portName = args[0]; } if(args.length > 2) { baud =Integer.parseInt(args[1]); } } ApplicationService aService = new ApplicationService( ApplicationService.CAPTURE_URL_DEFAULT, portName, baud); aService.processLoop(); } // main }ApplicationService.java
package org.dataparallel.device.driver; import java.io.BufferedInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.UnknownServiceException; import java.util.ArrayList; import java.util.List; /** * The ApplicationService class handles download and processing of HTTP data * to be downloaded to the hardware device. * @author mfobrien */ public class ApplicationService { private String captureURL = null; private boolean useHTTPProxy = true; private String port = null; private SerialDriver serialDriver; // lazy load the serial driver // number of total digits on the display device (currently 4 rows of 8) public int[] propellerLoadMessage = {10,10,2,6,6,9,1,2, 10,3,1,6,5,1,3,10, 10,2,2,4,1,9,2,10, 3,1,4,5,1,9,10,10}; // Invariant constants // buffer size when reading from the ftp source private static final int INPUT_BUFFER_SIZE = 1024; private static final int SLEEP_TIME = 15 * 60; public static final String CAPTURE_URL_DEFAULT = "https://bugs.eclipse.org/bugs/buglist.cgi?query_format=advanced;bug_status=ASSIGNED;component=Documentation;component=Examples;component=Foundation;component=JPA;classification=RT;product=EclipseLink"; // The current length of bug numbers - as of 2010 we are still at 316000 - we should not hit 999999 until around 2015 private static final int BUG_LENGTH = 6; public ApplicationService() { this(null, null, 0); } public ApplicationService(String aUrl) { this(aUrl, null, 0); } public ApplicationService(String aUrl, String aPort) { this(aUrl, aPort , 0); } public ApplicationService(String aUrl, String aPort, int aBuadRate) { captureURL = aUrl; port = aPort; // initialize logging if(useHTTPProxy) { // inside a firewall only System.getProperties().put("proxySet","true"); System.getProperties().put("proxyHost", "www-proxy.yourorg.com"); System.getProperties().put("proxyPort", "80"); } bugs = new ArrayList(); } /** * PUBLIC: * Return the URL that will be parsed for device data. * @return */ public String getCaptureURL() { // lazy load default if not set if(null == captureURL) { captureURL = CAPTURE_URL_DEFAULT; } return captureURL; } /** * Set the URL that will be parsed for device data. * @param aURL */ public void setCaptureURL(String aURL) { captureURL = aURL; } public SerialDriver getSerialDriver() { return getSerialDriver(null); } public SerialDriver getSerialDriver(String portOverride) { if(null == serialDriver) { if(null == portOverride) { serialDriver = new SerialDriver(port); } else { serialDriver = new SerialDriver(portOverride); } } return serialDriver; } public void resetBugs() { bugs = new ArrayList(); } private boolean write32digits(String portOverride) { for(int i=0;i<32;i++) { getSerialDriver(portOverride).push(i, propellerLoadMessage[i], portOverride); } return true; } private String captureBugList(String urlString) throws Exception { /** this stream is used to get the BufferedInputStream below */ InputStream abstractInputStream = null; /** stream to read from the FTP server */ BufferedInputStream aBufferedInputStream = null; /** stream to file system */ FileOutputStream aFileWriter = null; /** connection based on the aURL */ HttpURLConnection aURLConnection = null; /** URL object that we can pass to the URLConnection abstract factory */ URL aURL = null; long byteCount; // mark the actual bytes read into the buffer, and write only those bytes int bytesRead; String line; StringBuffer pageBuffer = new StringBuffer(); // regular expression objects try { // Clear output content buffer, leave header and status codes // throws IllegalStateException aURL = new URL(urlString); // get a connection based on the URL // throws IOException aURLConnection = (HttpURLConnection)aURL.openConnection(); // get the abstract InputStream from the URLConnection // throws IOException, UnknownServiceException abstractInputStream = aURLConnection.getInputStream(); aBufferedInputStream = new BufferedInputStream(abstractInputStream); // signed byte counter for file sizes up to 2^63 = 4GB * 2GB byteCount = 0; System.out.println("Downloading quote from: " + urlString); // buffer the input // Note: the implementation of OutputStream.write(,,) // may not allow the buffer size to affect download speed // Also: the byte array is preinitialized to 0-bytes // Range is -128 to 127 byte b[] = new byte[INPUT_BUFFER_SIZE]; // Read a specific amount of bytes from the input stream at a time // and redirect the buffer to the servlet output stream. // A -1 will signify an EOF on the input. // Start writing to the buffer at position 0 // throws IOException - if an I/O error occurs. while ((bytesRead = aBufferedInputStream.read( b, // name of buffer 0, // start of buffer to start reading into b.length // save actual bytes read, not default max buffer size )) >= 0) { /** * We will use the write() function of the abstract superclass * OutputStream not the print() function which is used for html * output and appends cr/lf chars. * Only write out the actual bytes read starting at offset 0 * throws IOException * - if an I/O error occurs. * In particular, an IOException is thrown if the output stream is closed. * IE: The client closing the browser will invoke the exception * [Connection reset by peer: socket write error] * IOException: Software caused connection abort: socket write error * * If b is null, a NullPointerException is thrown. * * Note: The default implementation of write(,,) writes one byte a time * consequently performance may be unaffected by array size */ // keep track of total bytes read from array byteCount += bytesRead; // read to \r\n, \r or \n line = new String(b); pageBuffer.append(line); } // while System.out.println("\nHTML capture/processing complete: bytes: " + byteCount); // we successfully streamed the file aBufferedInputStream.close(); aURLConnection.disconnect(); if(null != aFileWriter) { aFileWriter.flush(); aFileWriter.close(); } } catch (IllegalStateException e) { e.printStackTrace(); throw e; } catch (UnknownServiceException e) { // testcase: remove ftp prefix from the URL e.printStackTrace(); throw e; } catch (MalformedURLException e) { // testcase: remove ftp prefix from the URL e.printStackTrace(); throw e; } catch (IOException e) { // 403 testcase: add text after ftp:// e.printStackTrace(); throw e; } catch (Exception e) { e.printStackTrace(); } finally { // close input stream if(aBufferedInputStream != null) { aBufferedInputStream.close(); } // if // close file stream if(aFileWriter != null) { aFileWriter.flush(); aFileWriter.close(); } // dereference objects } // finally return pageBuffer.toString(); } // captureURL private void processPage(String page) { String searchBugFragment = "show_bug.cgi?id="; String searchPriorityFragment = ") int searchPosition = 0; int priorityIndex; int userIndex; // keep a potential bug around until i verify it is assigned properly on the next while loop pass BugNumber potentialBug = null; // clear bug list for repeated URL parsing resetBugs(); while(searchPosition < page.length()) { int thisBugPosition = page.indexOf(searchBugFragment, searchPosition); if(thisBugPosition < 0) { searchPosition += searchBugFragment.length(); } else { // get bug # String bugString = page.substring(thisBugPosition + searchBugFragment.length(), thisBugPosition + searchBugFragment.length() + BUG_LENGTH); // if we get a new bug # before matching the user - discard old one potentialBug = new BugNumber(bugString); // verify we did not skip an intervening bug # by checking for the next one int nextBugIndex = page.indexOf(searchBugFragment,thisBugPosition + searchBugFragment.length()); if(nextBugIndex < 0) { // we are at end of doc - no more bugs nextBugIndex = page.length() - 1; } // Bug # found, check "assigned" field between them userIndex = page.substring(thisBugPosition + searchBugFragment.length() + BUG_LENGTH, nextBugIndex).indexOf(searchUserFragment); if(!(userIndex < 0)) { System.out.println("Found " + potentialBug.getNumber()); bugs.add(potentialBug); } // move pointer searchPosition = thisBugPosition + searchBugFragment.length(); } } // get the Priority // keep the values if assigned to me } /** * Select 4 out of (x) bugs to send to device */ private void reduceAndPrepareBugList() { // sort by priority int bugCount = 0; int bugDisplayOffset = 1; // where to start writing bug digits // preload display buffer with position markers for(int i=0;i@lt;SerialDriver.NUMBER_DISPLAY_DIGITS;i++) { propellerLoadMessage[i] } for(BugNumber bug : bugs) { // preload display line with blanks for(int i=0;i<SerialDriver.NUMBER_DISPLAY_DIGITS_PER_LINE;i++) { propellerLoadMessage[bugCount * SerialDriver.NUMBER_DISPLAY_DIGITS_PER_LINE + i] = SerialDriver.CHAR_CODE_BLANKING; } // only display the first four bugs if(bugCount < SerialDriver.NUMBER_DISPLAY_LINES) { List<Integer> digits = bug.getIntDigits(); for(int i=0;i<BUG_LENGTH;i++) { propellerLoadMessage[ (bugCount * SerialDriver.NUMBER_DISPLAY_DIGITS_PER_LINE) + i + bugDisplayOffset] = digits.get((BUG_LENGTH - bugDisplayOffset) - i); } bugCount++; } } } /** * Download and process the message extracted from the URL to the device. * @param port */ public void processLoop() { processLoop(null); } public void processLoop(String port) { String page; try { for(;;) { page = captureBugList(getCaptureURL()); // try again if the service is down after 5 sec int retries = 10; while(retries-- > 0) { if(page == null || page.isEmpty()) { System.out.println("> connection timeout - try again in 5 sec"); Thread.sleep(5000); page = captureBugList(getCaptureURL()); } else { retries = 0; } } // process page processPage(page.toString()); // reduce bug list from x bugs to 4 reduceAndPrepareBugList(); // do it twice (as first 2 may not write - give time for FTDI to initialize) write32digits(port); write32digits(port); Thread.sleep(SLEEP_TIME * 1000); } } catch (Exception e) { e.printStackTrace(); } // try } public static void main(String[] args) { // Test applicationService ApplicationService aService = new ApplicationService(); aService.setCaptureURL(CAPTURE_URL_DEFAULT); // get port if passed in String portName = null; if(null != args && args.length > 0) { portName = args[0]; } for(;;) { aService.processLoop(portName); } } // main } // ApplicationService
Spin code:
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 VAR ' shared display RAM for the 4 display cogs long buffer[32] long Stack0[64] ' Stack Space long Stack1[64] ' Stack Space long Stack2[64] ' Stack Space long Stack3[64] ' Stack Space long Stack4[16] ' Stack Space long Stack5[16] ' Stack Space long Stack6[16] ' Stack Space byte Cog[6] ' Cog ID byte flashFlag ' visual clock bit long randomNum OBJ SER : "FullDuplexSerial" PUB main | mIndex, mValue, lRec, i, tPin {{ Each line of 8 LED 7-segments are driven by a 595 SIPO pair by a single cog No resistors are required as the inherent resistance of the 595 output pins is enough to limit the current below 20ma. The 3 pin parameters below are for the Shift Clock, Register Clock and Data pins. }} Cog[0] := cognew(Push8seg(0,buffer,2,3,4, 24_000,0), @Stack0) + 1 Cog[1] := cognew(Push8seg(8,buffer,5,6,7,50_000,0), @Stack1) + 1 Cog[2] := cognew(Push8seg(16,buffer,8,9,10,24_000,0), @Stack2) + 1 Cog[3] := cognew(Push8seg(24,buffer,11,12,13,30_000,0), @Stack3) + 1 ' echo Comm port data back to the sender ' comm control needs 2 cogs ser.start(31,30,0,38400) lRec := 48 i := 0 repeat ' wait for char lRec := ser.rx ' check for command 32 with index:value next if lRec == 32 ser.tx(32) ' get index lRec := ser.rx mIndex := lRec - 96 ' get value lRec := ser.rx mValue := lRec - 48 lRec := ser.rx ' check for EOL if lRec == 13 if i < 32 i := i + 1 else i := 0 ser.tx(mValue + 48) ser.tx(mIndex + 96) else ser.tx(lRec) ' this represents the value buffer[mValue - 48] := lRec {{ 7seg CA CC (China) blue=ca, red=cc g f + a b 1 2 3 4 5 -a f| |b -g e| |c -d.dp 10 9 8 7 6 e d + c dp }} PUB Write595BitD(Data2,cpin,dpin,delay) ' write data outa[dpin] := Data2 ' toggle clock outa[cpin] := 0 waitcnt(cnt + clkfreq / delay * 1) outa[cpin] := 1 waitcnt(cnt + clkfreq / delay * 1) PUB Write595Bit(Data2,cpin,dpin) ' write data outa[dpin] := Data2 ' toggle clock outa[cpin] := 0 outa[cpin] := 1 PUB Push8seg(dOffset, Data,cpin,rpin,dpin, delay,mux) | dValue,loop, index, muxOffset, dByte, blueOn, cBit, dBit, sft595, tempPartAddress, segValue, colVal, hiddenColumn ' LED display is driven by 2 595 shift registers ' 595-1 is the column driver ' 595-2 is the data driver dira[cpin] := 1 outa[cpin] := 0 dira[rpin] := 1 outa[rpin] := 0 dira[dpin] := 1 outa[dpin] := 0 ' Hardware configuration ' 76543210 <-- sweep direction ' BBBBRRRR(BlueCA/RedCC) ' In format --> shift direction ' 0.............15 ' gfab.cde0000c000 muxOffset := 0 repeat if muxOffset > 0 muxOffset := 0 else muxOffset := 1 repeat loop from 16 to 100 ' initially blank display ' shift 16 bits into 1 of 4 rows 0-7=column, 8-f=data ' blue 4 digits - common anode repeat dByte from 0 to 7'buffSize dValue := buffer[dByte + dOffset] ' do PWM if mux > 0 repeat 1'loop / 16 'waitcnt(clkfreq / 1000 + cnt) if muxOffset > 0 repeat 16 Write595Bit(0,cpin,dpin) ' push storage reg to output reg outa[rpin] := 0 outa[rpin] := 1 waitcnt(clkfreq / (delay) + cnt) if dByte < 4 colVal := 0 else colVal := 1 ' shift in data bits first repeat dBit from 0 to 7 'segValue := segDigit[8 * buffer[dByte] + (7 - dBit)] if dValue < 16 segValue := segDigit[(8 * dValue + (7 - dBit))] else segValue := segDigit2[(8 * (dValue - 16) + (7 - dBit))] if colVal < 1 Write595Bit(segValue,cpin,dpin) else Write595Bit(1 - segValue,cpin,dpin) ' shift in column bits last { ' 0...:...7 1000:1111 0100:1111 0010:1111 0001:1111 0000:0111 0000:1011 0000:1101 0000:1110 } ' skip initial blue hiddenColumn := 0 blueOn := 1 if dByte < 4 ' red ' 4 blank blue repeat 4 Write595Bit(1 - blueOn,cpin,dpin) repeat (3 - dByte) Write595Bit(blueOn,cpin,dpin) Write595Bit(1 - blueOn,cpin,dpin) repeat dByte Write595Bit(blueOn,cpin,dpin) else ' blue repeat (3 - (dByte - 4)) Write595Bit(1 - blueOn,cpin,dpin) Write595Bit(blueOn,cpin,dpin) repeat (dByte - 4) Write595Bit(1 - blueOn,cpin,dpin) ' 4 blank red repeat 4 Write595Bit(blueOn,cpin,dpin) ' push storage reg to output reg outa[rpin] := 0 outa[rpin] := 1 DAT 'gfab.cde ' 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z - + segDigit byte 0,1,1,1, 0,1,1,1, 0,0,0,1, 0,1,0,0, 1,0,1,1, 0,0,1,1, 1,0,1,1, 0,1,1,0, 1,1,0,1, 0,1,0,0, 1,1,1,0, 0,1,1,0, 1,1,1,0, 0,1,1,1, 0,0,1,1, 0,1,0,0, 1,1,1,1, 0,1,1,1, 1,1,1,1, 0,1,1,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 1,0,0,0
Running the code:
- download the SPIN code to the propeller microcontroller
- run the Java client on the Windows/Linux PC
c:\jdk1. 6.0_32bit\bin\java -cp .;javax.persistence_2.0.3.v201010191057.jar;eclipselink.jar;comm.jar;derbyclient.jar org.dataparallel.device.driver.ApplicationService COM15 Logs/Console Output
[EL Finest]: 2010-12-08 16:38:18.527--ServerSession(27379847)--Thread(Thread[main,5,main])--End deploying Persistence Unit dataparallel.derby; session file:/F:/wse/view_w35e/org.dataparallel.device.DisplayDriver/bin/_dataparallel.derby; state Deployed; factoryCount 1 Metamodel: MetamodelImpl@14105722 [ 6 Types: , 1 ManagedTypes: , 1 EntityTypes: , 0 MappedSuperclassTypes: , 0 EmbeddableTypes: ] Downloading quote from: https://bugs.eclipse.org/bugs/buglist.cgi?query_format=advanced;bug_status=ASSIGNED;component=Documentation;component=Examples;component=Foundation;component=JPA;classification=RT;product=EclipseLink HTML capture/processing complete: bytes: 68041 Found http://bugs.eclipse.org/307105 Found http://bugs.eclipse.org/325167 Found http://bugs.eclipse.org/326728 2010.8.12_16:38:20.385: COM15 opened for Propeller chip communications OUT: 10@0 OUT: 5@1 OUT: 0@2 OUT: 1@3 OUT: 7@4 OUT: 0@5 OUT: 3@6 OUT: 10@7 OUT: 10@8
Circuit Diagram:
Pending... along with refactored fault-tolerant code and JPA persistence for DB access.
Added Java JPA 2.0 persistence (PC side) via the EclipseLink 2.1.2 implementation of the spec. The database is currently Derby 10.5.3.0 - which ships with most application servers and the JDK itself.
Update: 20101121: The Propeller controller on this board will be replaced and enhanced with the Spinneret Web Server which contains both the 32-bit Propeller processor and a WIZnet W5100 TCP/IP Ethernet controller - allowing updates to occur over the network instead of via USB.
Also, this board relies on the host PC to gather internet data from a URL - with the Spinneret we can do this all in the embedded device (doing away with the PC alltogether). We will need proxy support though.
Parallax Spinneret product page:
http://www.parallax.com/Store/Microcontrollers/PropellerDevelopmentBoards/tabid/514/ProductID/710/List/0/Default.aspx?SortField=ProductName,ProductName
Parallax Spinneret discussion/development forum
http://forums.parallax.com/forumdisplay.php?f=82
http://forums.parallax.com/showthread.php?121494-Locating-amp-Identifying-propeller-chip-via-USB&p=1071158&viewfull=1#post1071158
http://forums.parallax.com/showthread.php?132944-Java-comm-and-basic-stamp&p=1071159&viewfull=1#post1071159
http://forums.parallax.com/showthread.php?109333-HowTo-Single-host-computer-and-multiple-propeller-serial-comm-and-programming&p=775612&viewfull=1#post775612
http://forums.parallax.com/showthread.php?137636-Quickstart-Board-reboots-when-PC-GUI-program-starts-and-stops&p=1071154&viewfull=1#post1071154
thank you
/michael