Graphics Question

> I think C and Python will cover my needs unless I end up back in IT. Python for adding graphics to say, machine vision apps and C for controls and working with hardware. <

As well as Pyrex, there's also SWIG and ctypes. Ctypes seems to be the popular way of doing things these days and it's a standard library module. However, the different methods have advantages in different applications, so there's no one best way of doing it. Ctypes is usually easier, but a proper wrapper has less overhead which may matter in some applications.
 
C

curt wuollet

I figured that horrible hacking might drive you to the keyboard:^) I'll have to study the tags bit. Actually we are thinking along the same lines, but I can see drawing and redrawing all the lines and text with three more channels might be a lot of code and the starting static and getting dynamic when you reach the scrolling point makes things messy. The way I did it would require each label to be an attribute so you can use canvas.remove

I've been looking at wall clock times on the chart divisions and that is quite a head scratcher too. The scoping and immutable variables has been a change for sure. I guess they are saying anything that belongs to a class instance should be an attribute, to use the vernacular. It's been a fairly long time since I hacked till the sun came up:^)

The approach that has come to mind is well beyond my python skills at the moment, but that's a good way to learn for me. My thought is to have two sheets of "graph paper" with the markings built in (loaded from a bitmap) each would be one canvas length. Show one and draw the variable on it and the labels as they are reached. Shift modes and scroll sheet 1 off the canvas as you scroll sheet 2 on, labeling and drawing the variable as you go. Rinse and repeat. That way you would only be drawing the variable and labels and moving the labels and you can simply reuse the two sheets of graph paper. I didn't see in my perusal of the Canvas widget how to do this, but pygame looked to have the stuff. Does that make any sense or am I thinking like an old C hack?

Regards,
cww
 
The grid works fairly well with the present scheme, which is to shift the lines and text, and then move the grid back and re-draw the next once per division (every 50 points). The grid numbers are meaningless at the moment, but that's just a scaling issue.

Your idea of "two sheets of graph paper" is what I was trying to describe as "alternative blocks". This would be used for just the data traces, as the grid seems to work fine with the existing system.

We would have two data traces which we'll call 'a' and 'b'. We start by drawing 'a', and add one line segment at a time (similar to the first example) rather than drawing the entire line with one list. We would also apply a tag of 'a' to each line segment.

When the chart was full, We start moving it to the left (using the tag), but now add new lines to 'b'. When 'a' is completely off the chart, we can delete it. Then we stop adding to 'b' and start adding to 'a' at the right of the chart again.

With this method we don't erase and re-draw the entire chart each time we add a point. We only draw one segment at a time, and only delete a plot once for every screen full.

The only thing really new required here is to have a rectangle at the left side of the chart which the line would pass under in order to hide the line as it passes off the chart. Alternatively, it might be possible to have a canvas on top of (or inside) a canvas so that the decorations are on one canvas, and the line plot on the other.

It will by the way be easier with this scheme if the plot starts from the right side of the chart and flows left rather than filling from the left and only then start scrolling. I've seen charts use both methods, and the current method is easier if you delete and re-draw the entire line each time. Starting from the right however is probably easier if we are moving the lines however, as the new lines always get drawn in the same place (the two X coordinates never change).

I'll do a new example of this and post it.
 
V

Victor Rocha

Hello,

There are a number of open-source solutions, "ready-made" for graphics, so you don't need to hack much around codes. I could mention PHPlot, GNUplot, JPgraph, Jfreechart and much others.

But in my opinion you could handle this task with a "ready-made" Scada or HMI open-source package (like PVbrowser which was already mentioned here).

I'm personally involved in one of these open-source SCADA's which is ScadaBR. Much of ScadaBR is based on Mango M2M by Serotonin software.

With Mango, you can much simply add your own datasources (modbus, sql, http and others supported), create a graphical View and add a timeseries Chart.

The chart will automatically scroll with grids, axis, legends etc. In ScadaBR we are even adding OPC support, based on OpenScada's Utgart libraries.

On the other hand, my personal choice if I were to roll out a quick solution on my own, would be picking an open-source chart library (for example phplot is much easy to use), then just "redraw" it in a couple of seconds.

Ah by the way, maybe you could check this page, www.digitemp.com --- digitemp reads 1-wire Dallas temperature sensors, but the project's author has added a very nice utility that plots the graphs using RRDtool. Worth taking a look. :)

best regards
Victor
 
Here's a version with some additional changes. This one adds individual line segments to the right side of the chart. It labels plots in alternating blocks. When a block of points has completely scrolled off the screen, they are all erased. There is a rectangle drawn on the left side of the chart, and the plot lines go *under* this rectangle. That's why you don't see them when they go off the left side of the plot area.

I made a few other changes. I converted the numeric literals to variables and then calculated the necessary derived values so that I could change one number and have everything automatically reflected elsewhere. I'm not 100% sure that I have all the equations correct however.

I also added three more plot lines, since you said you were using more than one.

I also found the graph to almost unreadable at the density that you originally used. There are simply too many points on the screen. I spread the points out further (2 pixels apart instead of 1), slowed the chart down (50 msec update instead of 25), made the chart bigger, and made the plot lines narrower. You might need to change that back if those numbers dont' suit your application. Even with these values I find the chart to be too dense for my tastes.

I also added a new class (object) to simulate collecting data. This is a more reasonable place to put the timed call-back (rather than in the plot code). It calls the plot code to update data. I put the data in a list (array) because that was more flexible if plots need to be added or removed. The graph object still needs to be initialised with the update interval however, so that it can calculate the correct X axis labels.

Another thing that has changed is that the canvas is now local to the graph object instead of being global. This is also a bit more realistic.

A new feature is that it is using tag_raise and tag_lower. These "raise" or "lower" objects in the graphics stack relative to each other. This is used to for example make the graph plots go under the rectangle which acts as a mask on the left side of the chart.

There are by the way lots of off the shelf plotting modules for Python, but they are normally intended for scientific use where you collect data and then display it. Most of these are just Python bindings for the same plotting libraries that everyone else uses.

There is also one called Blt which is part of Pmw (an additional widget library for Tkinter). It supposedly is a scrolling strip chart widget which should do the job exactly. However, I've never been able to get it to work (not even with their examples), so I can't recommend it.

If you're looking for numeric processing (ffts, matrix math, digital filters, etc.) there are libraries called Numpy and Scipy which are very widely used. They are mostly Python bindings to standard Fortran and C numerical libraries that everyone uses. Some of the expensive commercial libraries that you can buy are simply these same open source libraries bundled up and re-packaged.
<pre>
#!/usr/bin/python
# simple example that draws a scrolling line graph.
import Tkinter
import random

############################################################
class Graph:
"""We create a class to make it easier to keep static data between calls
"""


###################################################
def __init__(self, window, timescale):
"""window = Reference to window the canvas is to be placed in.
timescale = Update interval in milliseconds.
"""

# X offset for graph origin.
self._GXOffset = 40
# Width of graph itself.
self._GXWidth = 750
# Padding on right of graph.
self._GXPad = 25
# Height of graph.
self._GYHeight = 400
# Padding for bottom of graph.
self._YPad = 50
# Padding for top of graph.
self._YTopPad = 10
# X increment for grid.
self._GridXInc = 50
# Y increment for grid.
self._GridYInc = 25
# X axis resolution.
self._XResolution = 2
# Time increment for each update.
self._TimeInc = (self._GridXInc * timescale * 1.0) / (self._XResolution * 1.0) / 1000.0


# This creates the "canvas" object to draw on.
self._GrCanvas = Tkinter.Canvas(window, width=self._GXWidth + self._GXOffset + self._GXPad,
height=self._GYHeight + self._YPad + self._YTopPad, bg = 'white')
self._GrCanvas.pack()

# Place a rectangle to act as a mask for the graph.
self._GrCanvas.create_rectangle(0, 0, self._GXOffset, self._GYHeight + self._YTopPad,
outline='', fill='white', tag='mask')

# Draw the axes.
# X axis.
self._GrCanvas.create_line(self._GXOffset, self._GYHeight + self._YTopPad,
self._GXWidth + self._GXOffset,
self._GYHeight + self._YTopPad, fill='blue', width=2)
# Y axis.
self._GrCanvas.create_line(self._GXOffset, self._YTopPad,
self._GXOffset, self._GYHeight + self._YTopPad, fill='blue', width=2)

# Label the axes.
self._TextOffset = 0
self.Xlabel(self._TextOffset)
for y in range(0, self._GYHeight + self._GridYInc, self._GridYInc):
self._GrCanvas.create_text(self._GXOffset - 25, y + self._YTopPad,
text=(self._GYHeight - y), tag=('ylabels'))

# Draw the grid. Vertical lines.
for x in range(self._GridXInc, self._GXWidth + self._GridXInc, self._GridXInc):
self._GrCanvas.create_line(x + self._GXOffset, self._YTopPad, x + self._GXOffset,
self._GYHeight + self._YTopPad, fill='green', width=1, tag=('vgrid'))

# Tracks when the grid needs to shift.
self._GridPos = self._GridXInc


# Horizontal lines.
for y in range(0, self._GYHeight, self._GridYInc):
self._GrCanvas.create_line(self._GXOffset, y + self._YTopPad,
self._GXOffset + self._GXWidth, y + self._YTopPad, fill='grey', width=1, tag=('hgrid'))


# Position of current plot position.
self._PlotPos = self._GXWidth
# The current and previous plot tags.
self._CurrentTag = 'plota'
self._PreviousTag = 'plotb'

# The previous point we plotted.
self._PreviousYPoints = [(self._GYHeight / 2) + self._YTopPad] * 4
# The two X positions for each graph line segment that we draw.
self._XPoint1 = self._GXOffset + self._GXWidth - self._XResolution
self._XPoint2 = self._GXOffset + self._GXWidth

# Arrange the stacking order for the data.
self._GrCanvas.tag_raise('ylabels', 'mask')


###################################################
def UpdateGraph(self, datapoints):
"""datapoint = The new point to plot.
"""
# Shift the plot to the left.
self._ShiftPlot()
# Shift the grid to the left.
self.ShiftGrid()

# Append the new point to the graph.
self._GrCanvas.create_line(self._XPoint1, self._PreviousYPoints[0], self._XPoint2, datapoints[0],
fill='red', width=1, tag=self._CurrentTag)

# Append the new point to the graph.
self._GrCanvas.create_line(self._XPoint1, self._PreviousYPoints[1], self._XPoint2, datapoints[1],
fill='blue', width=1, tag=self._CurrentTag)

# Append the new point to the graph.
self._GrCanvas.create_line(self._XPoint1, self._PreviousYPoints[2], self._XPoint2, datapoints[2],
fill='yellow', width=1, tag=self._CurrentTag)

# Append the new point to the graph.
self._GrCanvas.create_line(self._XPoint1, self._PreviousYPoints[3], self._XPoint2, datapoints[3],
fill='violet', width=1, tag=self._CurrentTag)


# Make sure the data is below the mask rectangle.
self._GrCanvas.tag_lower(self._CurrentTag, 'mask')

# Save the point so we can plot it next time.
self._PreviousYPoints = datapoints


###################################################
def _ShiftPlot(self):
"""Shift the graph plot left one increment.
"""

# Shift both plots over one.
self._GrCanvas.move(self._CurrentTag, -self._XResolution, 0)
self._GrCanvas.move(self._PreviousTag, -self._XResolution, 0)
self._PlotPos -= self._XResolution

# Has the current plot block filled the screen entirely?
if (self._PlotPos < 0):
self._PlotPos = self._GXWidth

# Delete the *previous* plot block.
self._GrCanvas.delete(self._PreviousTag)

# Toggle the block selection
if (self._CurrentTag == 'plota'):
self._CurrentTag = 'plotb'
self._PreviousTag = 'plota'
else:
self._CurrentTag = 'plota'
self._PreviousTag = 'plotb'




###################################################
def Xlabel(self, textoffset):
""" Create the x axis labels for a graph.
Parameters: textoffset (integer) = An offst to add to the
label values.
"""
# Label the axes.
for x in range(0, self._GXWidth):
self._GrCanvas.create_text((x * self._GridXInc) + self._GXOffset + self._GridXInc,
self._GYHeight + self._YTopPad + 25,
text=((x + textoffset) * self._TimeInc), tag='xlabel')

###################################################
def ShiftGrid(self):
"""This shifts the grid one increment to the left each time it
is called. When the grid has moved one full grid increment, it
is shifted back and relabeled.
"""
# Move the vertical grid lines and labels to the left.
self._GrCanvas.move('vgrid', -self._XResolution, 0)
self._GrCanvas.move('xlabel', -self._XResolution, 0)
self._GridPos -= self._XResolution
# When the grid has moved one full grid increment, move it
# back to the start position.
if self._GridPos <= 0:
self._GridPos = self._GridXInc
self._GrCanvas.move('vgrid', self._GridXInc, 0)
# Delete the old labels and draw new ones.
self._GrCanvas.delete('xlabel')
self._TextOffset += 1
self.Xlabel(self._TextOffset)



###################################################
def PrintGraph(self):
"""Print the graph to a file.
"""
f = open('graphout.ps', 'w+b')
output = self._GrCanvas.postscript()
f.write(output)
f.close()



############################################################
class DataCapture:
""" Data could be read and processed here. This is a bit more realistic
than putting all the logic in the graph routines.
"""
###################################################
def __init__(self, twindow, interval):
"""Parameters: twindow = A reference to the window we want to use to attach
the call-back timer to.
interval (integer) = The desired update interval in milli-seconds.
"""
self._TWindow = twindow
self._UpdateInterval = interval


###################################################
def CaptureData(self):
"""Start capturing the data.
"""
# Capture some simulated data.
readings = []
readings.append(random.random() * 100 + 20)
readings.append(random.random() * 100 + 120)
readings.append(random.random() * 100 + 200)
readings.append(random.random() * 100 + 310)

# Update the graph with the new data.
SimpleGraph.UpdateGraph(readings)

# Call the update function again later.
self._TWindow.after(self._UpdateInterval, self.CaptureData)


# This creates the window.
root = Tkinter.Tk()
root.title('A simple graph.')

GraphFrame = Tkinter.Frame(root)

# Scan interval in milliseconds.
ScanInterval = 50

# This creates the graph object.
SimpleGraph = Graph(GraphFrame, ScanInterval)

GraphFrame.pack()

# This makes a "quit" button.
Tkinter.Button(root, text='Quit', command=root.quit).pack(side=Tkinter.LEFT)
Tkinter.Button(root, text='Print', command=SimpleGraph.PrintGraph).pack(side=Tkinter.RIGHT)

# This initialises the data capture routine.
DataGet = DataCapture(root, ScanInterval)
# Start getting data.
DataGet.CaptureData()


# This is the main event loop.
root.mainloop()
</pre>
 
C

curt wuollet

Here's what I cooked up with pygame today.

I'll have to send the .pngs to anyone who wants them. I don't think the list supports them.
<pre>
> import pygame, sys,os,math
> from pygame.locals import *
>
> pygame.init()
>
> window = pygame.display.set_mode((700,1000 ))
> pygame.display.set_caption('Chart Recorder')
> background = pygame.image.load("gpb.png")
> cleanp = pygame.image.load("gp.png")
> paper1 = pygame.Surface((700,1000),0,background)
> paper2 = pygame.Surface((700,1000),0,background)
> pp = [paper1,paper2]
> pp[0] = pygame.Surface.copy(cleanp)
> pp[1] = pygame.Surface.copy(cleanp)
>
> screen = pygame.display.get_surface()
>
> screen.blit(background,(0,0))
> screen.blit(paper1, (0,0))
> pygame.display.flip()
> z = 150
> degree = 0
> while 1 :
> for x in range(700):
> screen.blit(background,(0,0))
> screen.blit(pp[0],(-x,0))
> screen.blit(pp[1],(700-x,0))
> z = (math.sin(math.radians(degree)) *50) + 200
> pygame.draw.line(pp[1],(255,0,0),(x -1,z),(x,z),1)
> pygame.draw.circle(screen,(255,0,0),(700,z),5,0)
> pygame.display.flip()
> degree += 1
> if degree == 360:
> degree = 0
> pp.reverse()
> pp[1] = pygame.Surface.copy(cleanp)
> def input(events):
> for event in events:
> if event.type == QUIT:
> sys.exit(0)
> else:
> print event
>
> while True:
> input(pygame.event.get())
</pre>
It works and I don't think it will get any slower with the rest of the channels implemented as the heavy lifting is in the blits and flips.

Regards
cww
 
curt wuollet said: "I'll have to send the .pngs to anyone who wants them. I don't think the list supports them."

Post them on some place like Tinypic, Imageshack, or some other image hosting service. That's what those places are meant for.

I don't have the image so I haven't run it, but I think you need a time.sleep() in it somewhere to control the pace (unless Pygame has its own equivalent). Also, I assume you want to put the drawing loop into a function and call one iteration at a time from the event input handler.
 
C

curt wuollet

Tes,

I am running it "wide open" so I can see changes in a reasonable amount of time. It seems no one library has all the features to make this easy. Pygame doesn't seem to have any obvious means to write the system time on as labels, or indeed any facility for easily dropping text on the sheets. The big feature for this approach is that the sheets can be any graphic. I created the pngs in Inkscape and could do even log or semi log or even store a chart form for each scale with the markings embedded, which I may do. But I still need to do the time axis and for HVAC that should be wall clock time. Pygame does, logically, have means to read the game port for the poor man's thermistor DAQ. I'm not sure whether the math is up to the parallel linear equations in three variables to linearize thermistors on the fly, but I'm not sure I am anymore either :^). A lookup may be in order. One very interesting thing is that it's faster in X than SDL with software drawing which is faster than SDL with HARDWARESURFACE drawing. Very strange. I'll mail you the pngs.

Regards,
cww
 
C

Curt Wuollet

Yes, the performance is really good on this one. I may switch back. I'm sure some of it is the small window. I'm doing a 1000x700 page for hi res hardcopy. Actually I will probably mess around with both and I'm getting accustomed to python in any case.

For the printing, the early TP version did line by line on a dot matrix printer. For today's page printers a directory of screens is probably more appropriate so they can be reviewed in any viewer and the ones of interest printed. Not exact CR emulation, but cheaper:^) I should look into a cheap DAQ card, the modern game ports drift quite a bit. In the XT days they were really surprisingly accurate for this app.

Regards
cww
 
Here's some handy documentation links:

The official Python documentation. This is for the version 2.6. For other versions see the links at the upper left.
http://docs.python.org/index.html

This is a direct link to the library reference. I keep this link handy:
http://docs.python.org/library/index.html

Links to other Tkinter information:
http://wiki.python.org/moin/TkInter

This is a (incomplete) Tkinter reference:
http://effbot.org/tkinterbook/

Here's another:
http://infohost.nmt.edu/tcc/help/pubs/tkinter/

This is a summary of Tkinter:
http://www.astro.washington.edu/users/rowen/TkinterSummary.html

This is a link to the publisher of the book that I mentioned earlier (Python and Tkinter Programming)
http://www.manning.com/grayson/

Even if you don't want the book, it has two sample chapters as PDF files, and all the source code examples as a ZIP file. One of the sample chapters you can download happens to be "Graphs and charts", and one of the examples happens to be how to do strip charts. The strip chart example is oriented towards weather data and uses the "delete and redraw" approach that we tried previously (it actually does it in a different way than I did however). The example is not directly usable in your application, but the chapter is worth reading (as I said, it's an excellent book on GUI programming).

As for printing, the example that I posted earlier prints to a postcript file. I didn't set any options however. You can set scale factors and rotation. There are two printing options which I don't see listed in the references that I linked above, and these are "pageheight" and "pagewidth". These are X and Y values with specify the size to which the finished plot should be scaled on the page. Since all the parameters are optional, you would specify them by name. E.g. output = self._GrCanvas.postscript(rotate=True)

If you are interested in other SDL libraries there is also one called Pyglet. I'm not familiar with it, so I don't know how it differs from Pygame. It's often called "equivalent to Pygame" however.
 
C

curt wuollet

Thanks Michael

I did find out how to blit text onto the pages. I haven't found any documentation which is curious, but I can copy the examples. It seems there is considerable detail that isn't documented, but passed from hack to hack.

Here's a snippet
<pre>
# Display some text
font = pygame.font.Font(None, 16)

ttext = font.render("+100", 1, (10, 10, 10))
textpos = ttext.get_rect()
#textpos.centerx = pp[1].get_rect().centerx
#textpos.centery = pp[1].get_rect().centery
textpos.centerx = 20
textpos.centery = 95
pp[1].blit(ttext, textpos)
</pre>
So what type is ttext? rect?, Font? Lard? (it came from rendering :^)) Haven't seen the docs on the centerx and centery methods. No matter, I've figured out what is going on, but it would be good to know what other methods there might be.

The wife knows I'm programming and therefore oblivious, so I'll have to mow the yard to pretend I'm not obsessed.

Regards
cww
 
According to the documentation, "The font module allows for rendering TrueType fonts into a new Surface object." The documentation on "Surface" has a "get_rect" method, along with 3 or 4 dozen others. I don't know if all of those would be meaningful for a font, but it looks like you can do just about anything with a font that you can with any other graphic - the font is just rendered as a bunch of pixels after all.

The pygame documentation is at "http://www.pygame.org/docs/"

The Debian package also installs the docs on your PC when you install python-pygame. Your RPM may have done the same.
 
C

curt wuollet

I finally came across a comment on the docs that mentioned the same problem with finding all the stuff you can do.

python
>>>> import pygame
>>>> dir(pygame.Rect)

lists a whole bunch of stuff not necessarily mentioned in the docs. Probably someplace but I haven't found it, yet.

Regards
cww
 
C

curt wuollet

Well, the guy called and asked how it's going, so I sent a demo. It does what it needs to do as far as presentation is concerned. The project is running into massive cost overruns though. Because his target PC, like my Dell has neglected to include a game port, I was forced to order a PCI sound card with a game port, that being about the only way to get a game port anymore. That cost $5.65 delivered. And I couldn't find my stock of thermistors so I had to spend another $6 and change to buy 40 1% 10k thermistors. So the R&D expenditure is getting close to $12. But the thermistors should be good for 10 systems.

I didn't post the program this time, I will if anyone is following along. Right now, it does the job, but it's not OO and it's not structured. And it's not apparent right at the moment what syntax I need to use for the joystick stuff. I need to study these topics while I wait for the hardware to arrive from the PRC or Hong Kong or wherever. And I'm looking at alternative A/D since the game port seems to have been deprecated. Dataq has some $25 serial units that should work fine but they're over on the dark side and only support Windows. I can use it and RE the protocol, but I will see if I can find more Open minded folks to deal with first. So it's off to the books for a while. Eventually I might generalize it and with Michael's OK for anything I copied, I'll release it GPL.

Regards,
cww
 
K

Ken Emmons Jr.

I'd also stay away from the gameport. A lot of guys are using a USB to RS-232 chip in their projects, so you can get into modern era and even laptop computer territory this way (Check out the chips they use on the Arduino project, I think that is how they do USB...) Drivers shouldn't be an issue as those guys support linux.

You should be able to get a RS-232 A/D going with a AVR or PIC for under $25 with a protoboard, etc... I'm sure someone has some kit boards out there for cheap on this front. Of course if you go your own way and don't have a programmer for the AVR/PIC you'll have to dish out $25-$50 for that. If you make a bunch you will need to have some boards made though.

Having said that about finding a project out there for the RS-232 A/D kit, I have been surprised that some of these "Open" projects are charging huge sums of money for their kits and PCBs. I know that is how they make money, but some of these guys are really trying to gouge you on price. I looked into a project to do a cheap digital readout on my machine tools (using those inexpensive chinese caliper scales) and the guy wants something like $50 for just the PCB (it was a small PCB), and I know in even moderate volume those things are only worth $8 or so. By the time I'd be done buying all the components and spending time I'd rather spend the money on glass scales on fleabay and use a real encoder interface with some precision (and maybe some accuracy)! Maybe I should spark my own project, but I have too many at the moment!

Sorry for the digression... :eek:)

~KEJR
 
Any of the code examples that I have posted for this topic can be used as you see fit without credit or attribution.

As for the Dataq serial module, they have a GPL Linux developer's kit. Just look for their Linux software web page. The download link is in a posting on their web forum, which is the same way they seem to do any of their other no-charge software (perhaps their e-commerce partner wants a commission on downloads through the web store).

You might want to just look at the source code (C++) and figure out the serial protocol. That would let you just integrate the communications directly in the Python program rather than compiling their source code and then calling that. I'm assuming that you can just do a simple poll/response cycle. I would suggest asking some questions on their Linux developer's forum.
 
C
Thank you Michael,
I have a boiler plate C serial reader that I can use, given the protocol. I've used it for a dozen such things. For $25 they might just blast readings at the host. But, if I go that way, I will probably at least try the Python facilities to make synchronization easy.

And in reply to Ken?, yes, I have thought of using Arduino or the kool Arm6 clone that has 7 A/D channels, but a packaged unit for $25 is more attractive in the short term. Right now, to roll the demo into a product, I need to reorganize and structure it and make it event driven as the clear way to get accurate timing is to set a timer to put events on the queue so the time to update the screen is not a factor, running in parallel as it were. Hopefully, crazy mousing, etc. won't swamp it and affect the timing too much. I don't have to do the generalization as what is wanted is a dedicated box that you start up and it does it's thing, 4 channels, 60 to 80 degrees F, time: minutes per division to hours per division, TBD. I'm sticking with the bitmap paper as the hardcopy is impressive. I'll send the latest hack and screens. You need not burn any time on it as I am already much in your debt.

One humorous note is that it's somewhat slower than TP2 on DOS on a 5 MHz 8088. But it does look nicer :^). If you compiled to a com file 99% less code would be running.

Regards
cww
 
C
Well after checking out the Dataq offerings fairly thoroughly, I do have to apologize to them because they do have a Linux SDK. It seems to me to be an extremely complex piece of code for what it does, which isn't much, and it really doesn't do what I want to do as the gadget controls the timing. And the bits are pretty well scrambled. I can't really say I want to support it. So, while waiting for the game port and thermistors, I've investigated the uPs that have A/D built in and require minimal external hardware. Since these are actually small standalone systems, there are a legion of trade offs. I finally came up with one that should do.

http://www.nkcelectronics.com/freeduino-serial-v20-board-kit-arduino-diecimila-compatib20.html

It has supporting software available for Linux and that other stuff. It's inexpensive, and has 6 10 bit A/D channels. It's a serial (RS232) version so it can be a ways away from the PC, it uses a real 232 chip so it should be more reliable than the kludges and has enough of a following where it should be around for a while. Folks that are so inclined can get a USB version, but I hate dongles and it's a lot easier to make a 232 cable on site if needed, cheaper too. And USB connectors aren't remarkably robust. The software is all OSS and I'm checking on the board, but since the Arduino designs are are CC share and share alike I don't think there are any IP issues. The kit is $17 US., so the whole package can be had for probably $10 a channel allowing for cabling, etc. I'm not real keen on the IDE and language, but it is simple and the whole ball of wax exists already. A no calibration version could be built, but that would require a quad Op Amp and LM35s or 36s as they are millivolt output and the A/Ds are +5V fs. I will have to come up with a protocol, but that will be Open as well. Something like: Send start character, look for ack, receive 8 bytes, ack. The other side will be similar.

Regards,
cww
 
C
Just by way of mentioning how this resolved: I ordered a game card, but it hasn't come yet. So I investigated uPs with analog and serial built in. Ordered a Freeduino serial version 2 for the princely sum of $16. It came, I assembled it, and I have it running providing input to the recorder. Here's the highly complex program in the Arduino Sketch language which is basically a thin veneer over C.
<pre>
// Program to use Freeduino as a 4 channel analog remote
// unit for the chart recorder. Mostly copied straight from
// the examples

int firstSensor = 0; // first analog sensor
int secondSensor = 0; // second analog sensor
int thirdSensor = 0; // third analog sensor
int fourthSensor = 0; // fourth analog sensor
int inByte = 0; // incoming serial byte

void setup()
{
// start serial port at 9600 bps:
Serial.begin(9600);

}

void loop()
{
// if we get a valid byte, read analog ins:
if (Serial.available() > 0) {
// get incoming byte:
inByte = Serial.read();
// read first analog input
firstSensor = analogRead(0);
// delay 10ms to let the ADC recover:
delay(10);
// read second analog input
secondSensor = analogRead(1);
// delay 10ms to let the ADC recover:
delay(10);
// read third analog input
thirdSensor = analogRead(2);
// delay 10ms to let the ADC recover:
delay(10);
// read fourth analog input
fourthSensor = analogRead(3);
// send sensor values:
Serial.println(firstSensor);
Serial.println(secondSensor);
Serial.println(thirdSensor);
Serial.println(fourthSensor);
}
}</pre>
While I'm not crazy about the idea of inventing a new "vanity" language rather than simply using the standards, I do have to admit that it is a very simple program, and it does the job. It lacks error checking, etc. etc. But $16 and 20 lines of code to add 6 10 bit analog channels to a PC, (I'm only using 4) is a pretty good deal. I wrote pyserial code on the other end to grab the readings. I had to stick to the most basic routine as the pyserial code didn't work as expected when I got fancy. That is, I'm sure it works, but will require study to do what I want. I wrote code in C to find the Hart Steinhart coefficients for the thermistor and generate a lookup table but I believe I'll try to write the R to T equation into the recorder rather than do lookup. All I need now for a fully functional version are the 10k 1% series resistors for the thermistors. Still need someplace to put all this stuff up to share.

Regards
cww
 
Curt,

I agree with your distaste of what you call "vanity languages", its how I feel about IEC structured text. I also thought the "sketch" and "wiring" veneer to be laughable, but it does work out well. Really the Arduino setup is C at the core with a more high level library structure. I might give this board you bought a try as I have a AVR development kit, but I hardly ever use it because I don't have any deployment boards and have to stop and think about how to use the environment every time I sit down with it...

KEJR
 
Top