World’s cheapest SCADA device: source code

This is the source code (as of May 27, 2008) for the Parallax BasicStamp Recording light meter.  I apologize for the formatting, but in the exciting new world of Office 2007, there is *literally* no way to edit the HTML that Google or Help know about, so I can’t use the  <pre></pre> tag.

' {$STAMP BS2}
' {$PBASIC 2.5}

index VAR Nib

wStrength VAR Word
wCount VAR Word

soundTime VAR Word
bCmd VAR Byte

'  generic tmp variable
iTmp VAR Word
wTmp VAR Word

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'  '
' ''' Number of values to store in EEPROM
'  '
'
wMaxValuesToStore VAR Word
wMaxValuesToStore = 200
wCurrEntvalueLocation VAR Word
wStoredValuesCount VAR Word
wStoredValuescount = 0

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'  '
' ''' How long to wait (milliseconds) for command input before resuming the main loop
'  '
'
wCommandDelay VAR Word
wCommandDelay = 3000

OUTH = %00000000
DIRH = %11111111

UNUSED_DATA CON 65535
'
'  Commands that can be sent to the application
'
CMD_RESET CON 0
CMD_DUMP_LAST_VALUE CON 1
CMD_DUMP_ALL_VALUES CON 2
CMD_SLEEP CON 3
'  returns a fixed length buffer with known contents
CMD_DIAG CON 4
CMD_NOOP CON 254

bCmd = CMD_NOOP

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'   ''
' ''''''
' ''''''
'   ''
'
GOSUB Reset

DO
  GOSUB getRCTime
  GOSUB getCommand
  GOSUB Delay
  GOSUB updateDisplay
LOOP

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
Reset:
  DEBUG "ResetStart",CR

  '  Make sure history memory ring buffer is set to a value that lets us later distinguish it from zero (0)
  DEBUG "Starting",CR
  DEBUG "Will save ", DEC wMaxValuesToStore, " values in EEPROM",CR

  DEBUG "Initializing history ring buffer",CR
  FOR iTmp = 0 TO ((wMaxValuesToStore -1) * 2) STEP 2
    WRITE iTmp, Word UNUSED_DATA
    'DEBUG DEC iTmp, "."
  NEXT
  DEBUG "Finished initializing history ring buffer",CR
  'DEBUG CLS, HOME
  wCount = 0
  wStrength = 0
  FOR iTmp = 0 TO ((wMaxValuesToStore -1) * 2) STEP 2
    WRITE iTmp, Word 0
  NEXT
  wStoredValuesCount = 0
  DEBUG "ResetFinish",CR
  RETURN

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
getCommand:

  'DEBUG "cmd> "
'  SERIN 16,  84,getCommandBadData, 5000,getCommandTimeout, [WAITSTR cmdBuffer9]
  SERIN 16,  84, wCommandDelay, getCommandTimeout, [DEC1 bCmd]
  'SERIN 16,  84, [DEC bCmd]
  'DEBUG DEC bCmd
  GOSUB processCommand
  RETURN

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
processCommand:
  'DEBUG "Got command: ", DEC bCmd, CR

  SELECT bCmd

    '  reset
    CASE CMD_RESET
      GOSUB Reset

    '  send current value
    CASE CMD_DUMP_LAST_VALUE
      DEBUG "", DEC5 wStrength, "", CR

    '  send last N values
    CASE CMD_DUMP_ALL_VALUES
'      DEBUG CLS, HOME
      DEBUG "" ,CR
      DEBUG TAB, "", DEC wMaxValuesToStore, "",CR
      DEBUG TAB, "", DEC wStoredValuesCount, "", CR
      DEBUG TAB, "", DEC (wCurrentValueLocation / 2), "",CR
      DEBUG TAB, "", DEC wCommandDelay, "", CR

'      FOR iTmp = 0 TO ((wMaxValuesToStore -1) * 2) STEP 2
      FOR iTmp = 0 TO ((wStoredValuesCount *2 ) + 1) STEP 2
        READ iTmp, Word wTmp
        IF wTmp = UNUSED_DATA THEN EXIT
        DEBUG TAB,"", CR
        DEBUG TAB, TAB,"", DEC iTmp, "", CR
        DEBUG TAB, "", CR
        wtmp = 0

      NEXT
      DEBUG "", CR

    CASE CMD_SLEEP
      DEBUG "Sleeping", CR
      SLEEP 1

    CASE CMD_DIAG
      DEBUG "deadbeef0",CR

    CASE CMD_NOOP
      'DEBUG DEC wCurrentValueLocation,"->",DEC wStrength,CR

      'DEBUG "Received NOOP command", CR
'      NAP 0
      DEBUG 0, CR

    CASE ELSE
      'DEBUG "Received unknown command", CR

    ENDSELECT

  RETURN

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
getCommandBadData:
  'DEBUG "Got bad command", CR
  DEBUG "CommandError", CR
  RETURN

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
getCommandTimeout:
  'DEBUG "No command received" ,CR
'     DEBUG DEC wCurrentValueLocation,"->",DEC wStrength,CR
      'DEBUG DEC wStrength,CR

  RETURN

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
getRCTime:
  '  These next 3 lines are the actual application.  How long is it taking for a charged cap to drop to 0?
  HIGH 2   '  arbitrary short time to charge the cap (from the training example)
  PAUSE 3  '  arbitrary short time to let it "leak" down to zero (from the training example)
  RCTIME 2, 1, wStrength

  soundTime = wStrength * 3

  'DEBUG  "Raw strength: ", DEC wStrength ,CR
  '22wStrength = wStrength * wStrength

  wCurrentValueLocation = (wcount // wMaxValuesToStore) * 2
  WRITE wCurrentValueLocation, Word wStrength
  wStoredValuesCount = (wStoredValuesCount + 1) MAX wMaxValuesToStore

  wCount = wCount + 1

'  DEBUG "", DEC5 wStrength, "", CR

' DEBUG CLS, DEC5 sStrength, " soundTime: ", DEC5 soundTime
' FREQOUT 5,  soundTime MAX 250 , ABS (soundTime - 2640)

  RETURN

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
Delay:
  PAUSE  wStrength MAX 250
  RETURN

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
updateDisplay:
  IF index = 6 THEN index = 0
  LOOKUP index, [ %01000000,
    %10000000,
    %00000100,
    %00000010,
    %00000001,
    %00100000 ], OUTH

  index = index + 1
  RETURN
Advertisements

The world’s cheapest SCADA device?

As part of my learning about CygNet, SCADA, and the exciting world of Real World Data, I decided to build a SCADA-enabled device from scratch.

I wanted something that could be reconfigured to support any sort of sensor, was very small, and that didn’t require soldering.  A little bit of research (and by research I mean shopping) turned up the venerable BasicStamp: http://www.parallax.com/Default.aspx?tabid=295

It’s just about the simplest embedded system possible.  A processor with a built-in Basic (the language) interpreter, 16 I/O lines, a serial port, and a small breadboard for components.

For $80, you can pick up a version at Radio Shack that includes a complete training program.   (http://www.radioshack.com/product/index.jsp?productId=2117994&cp=&sr=1&origkw=parallax&kw=parallax&parentPage=search), or you can buy just the boards for anywhere from $10-$40 on eBay or the Parallax store.

I spent a little over a week going through the training and ended up with a photocell-based light meter that output readings on the serial port, and stored a small number of recent readings in its onboard EEPROM.  I hadn’t read a circuit diagram in about 30 years, so that was fun.

Useful tidbit:  the web has lots of resistor value calculators

ParallaxBasicStampSCADA002

Doug had already created a serial-device EIE for the Davis Vantage weather station, so it was a matter of hours for him to add a new “general” message that would allow communications with arbitrary devices

Protocol

The protocol is blindingly simple. Send a number (really a single byte with the ASCII code for a numeral) from 0-n, and the device sends back data and/or takes some action. The current command set:

  • 0 (48/0x30) – reset the device. Clear the ring buffer and all counters and restart the program
  • 1 (49/0x31) – return the most recent reading
  • 2 (50/0x32) – return all readings
  • 3 (51/0x33) – sleep (turn off power to the device for N seconds, where N is currently hardcoded)
  • 4 (52/0x34 – return a known diagnostic string (“deadbeef”). Why that word? It’s the default register & memory settings for the RS/6000 plus it’s all in hex. I didn’t say the commands made sense.
  • 254 – used in early testing

Example: Send “1” -> Receive “NNNNN” where N is a numeral

There is no way I could have gotten this running without help from everyone, but especially Doug, Pete, Sheri, and Derek.

C# (.Net) example of wrapping CygNet COM objects

This is a complete (sources w/o binaries, but including an installer for the sample application) showing one way to expose CygNet entities via .NET

You must check with Lynne Whitehorn or Jeff Brady before distributing this code outside the company.  Please. 🙂

Sources (links work only from CygNet LAN/VPN since they point to slodat01dept_sharesResearch)