2015-03-24 00:33:19 -07:00
// picoExtension.js
// Shane M. Clements, February 2014
// PicoBoard Scratch Extension
//
// This is an extension for development and testing of the Scratch Javascript Extension API.
2016-08-02 00:49:34 -07:00
( function ( ext ) {
2015-03-24 00:33:19 -07:00
var device = null ;
var rawData = null ;
// Sensor states:
var channels = {
slider : 7 ,
light : 5 ,
sound : 6 ,
button : 3 ,
'resistance-A' : 4 ,
'resistance-B' : 2 ,
'resistance-C' : 1 ,
'resistance-D' : 0
} ;
var inputs = {
slider : 0 ,
light : 0 ,
sound : 0 ,
button : 0 ,
'resistance-A' : 0 ,
'resistance-B' : 0 ,
'resistance-C' : 0 ,
'resistance-D' : 0
} ;
// Hats / triggers
2016-08-02 00:49:34 -07:00
ext . whenSensorConnected = function ( which ) {
2015-03-24 00:33:19 -07:00
return getSensorPressed ( which ) ;
} ;
2016-08-02 00:49:34 -07:00
ext . whenSensorPass = function ( which , sign , level ) {
2015-03-24 00:33:19 -07:00
if ( sign == '<' ) return getSensor ( which ) < level ;
return getSensor ( which ) > level ;
} ;
// Reporters
2016-08-02 00:49:34 -07:00
ext . sensorPressed = function ( which ) {
2015-03-24 00:33:19 -07:00
return getSensorPressed ( which ) ;
} ;
2016-08-02 00:49:34 -07:00
ext . sensor = function ( which ) {
return getSensor ( which ) ;
} ;
2015-03-24 00:33:19 -07:00
// Private logic
function getSensorPressed ( which ) {
if ( device == null ) return false ;
if ( which == 'button pressed' && getSensor ( 'button' ) < 1 ) return true ;
if ( which == 'A connected' && getSensor ( 'resistance-A' ) < 10 ) return true ;
if ( which == 'B connected' && getSensor ( 'resistance-B' ) < 10 ) return true ;
if ( which == 'C connected' && getSensor ( 'resistance-C' ) < 10 ) return true ;
if ( which == 'D connected' && getSensor ( 'resistance-D' ) < 10 ) return true ;
return false ;
}
function getSensor ( which ) {
return inputs [ which ] ;
}
var inputArray = [ ] ;
2016-08-02 00:49:34 -07:00
2015-03-24 00:33:19 -07:00
function processData ( ) {
var bytes = new Uint8Array ( rawData ) ;
inputArray [ 15 ] = 0 ;
// TODO: make this robust against misaligned packets.
// Right now there's no guarantee that our 18 bytes start at the beginning of a message.
// Maybe we should treat the data as a stream of 2-byte packets instead of 18-byte packets.
// That way we could just check the high bit of each byte to verify that we're aligned.
2016-08-02 00:49:34 -07:00
for ( var i = 0 ; i < 9 ; ++ i ) {
var hb = bytes [ i * 2 ] & 127 ;
2015-03-24 00:33:19 -07:00
var channel = hb >> 3 ;
2016-08-02 00:49:34 -07:00
var lb = bytes [ i * 2 + 1 ] & 127 ;
2015-03-24 00:33:19 -07:00
inputArray [ channel ] = ( ( hb & 7 ) << 7 ) + lb ;
}
if ( watchdog && ( inputArray [ 15 ] == 0x04 ) ) {
// Seems to be a valid PicoBoard.
clearTimeout ( watchdog ) ;
watchdog = null ;
}
2016-08-02 00:49:34 -07:00
for ( var name in inputs ) {
2015-03-24 00:33:19 -07:00
var v = inputArray [ channels [ name ] ] ;
2016-08-02 00:49:34 -07:00
if ( name == 'light' ) {
2015-03-24 00:33:19 -07:00
v = ( v < 25 ) ? 100 - v : Math . round ( ( 1023 - v ) * ( 75 / 998 ) ) ;
}
2016-08-02 00:49:34 -07:00
else if ( name == 'sound' ) {
2015-03-24 00:33:19 -07:00
//empirically tested noise sensor floor
2016-08-02 00:49:34 -07:00
v = Math . max ( 0 , v - 18 ) ;
v = ( v < 50 ) ? v / 2 :
2015-03-24 00:33:19 -07:00
//noise ceiling
2016-08-02 00:49:34 -07:00
25 + Math . min ( 75 , Math . round ( ( v - 50 ) * ( 75 / 580 ) ) ) ;
2015-03-24 00:33:19 -07:00
}
else {
v = ( 100 * v ) / 1023 ;
}
inputs [ name ] = v ;
}
//console.log(inputs);
rawData = null ;
}
2016-08-02 00:49:34 -07:00
function appendBuffer ( buffer1 , buffer2 ) {
var tmp = new Uint8Array ( buffer1 . byteLength + buffer2 . byteLength ) ;
tmp . set ( new Uint8Array ( buffer1 ) , 0 ) ;
tmp . set ( new Uint8Array ( buffer2 ) , buffer1 . byteLength ) ;
2015-03-24 00:33:19 -07:00
return tmp . buffer ;
}
// Extension API interactions
var potentialDevices = [ ] ;
2016-08-02 00:49:34 -07:00
ext . _deviceConnected = function ( dev ) {
2015-03-24 00:33:19 -07:00
potentialDevices . push ( dev ) ;
if ( ! device ) {
tryNextDevice ( ) ;
}
2016-08-02 00:49:34 -07:00
} ;
2015-03-24 00:33:19 -07:00
function tryNextDevice ( ) {
// If potentialDevices is empty, device will be undefined.
// That will get us back here next time a device is connected.
device = potentialDevices . shift ( ) ;
2015-07-24 17:18:51 +00:00
if ( device ) {
2016-08-02 00:49:34 -07:00
device . open ( { stopBits : 0 , bitRate : 38400 , ctsFlowControl : 0 } , deviceOpened ) ;
2015-07-24 17:18:51 +00:00
}
}
var poller = null ;
var watchdog = null ;
2016-08-02 00:49:34 -07:00
2015-07-24 17:18:51 +00:00
function deviceOpened ( dev ) {
if ( ! dev ) {
// Opening the port failed.
tryNextDevice ( ) ;
return ;
}
2016-08-02 00:49:34 -07:00
device . set _receive _handler ( function ( data ) {
2015-03-24 00:33:19 -07:00
//console.log('Received: ' + data.byteLength);
2016-08-02 00:49:34 -07:00
if ( ! rawData || rawData . byteLength == 18 ) {
rawData = new Uint8Array ( data ) ;
} else {
rawData = appendBuffer ( rawData , data ) ;
}
2015-03-24 00:33:19 -07:00
2016-08-02 00:49:34 -07:00
if ( rawData . byteLength >= 18 ) {
2015-03-24 00:33:19 -07:00
//console.log(rawData);
processData ( ) ;
//device.send(pingCmd.buffer);
}
} ) ;
// Tell the PicoBoard to send a input data every 50ms
var pingCmd = new Uint8Array ( 1 ) ;
pingCmd [ 0 ] = 1 ;
2016-08-02 00:49:34 -07:00
poller = setInterval ( function ( ) {
2015-03-24 00:33:19 -07:00
device . send ( pingCmd . buffer ) ;
} , 50 ) ;
2016-08-02 00:49:34 -07:00
watchdog = setTimeout ( function ( ) {
2015-03-24 00:33:19 -07:00
// This device didn't get good data in time, so give up on it. Clean up and then move on.
// If we get good data then we'll terminate this watchdog.
clearInterval ( poller ) ;
poller = null ;
device . set _receive _handler ( null ) ;
device . close ( ) ;
device = null ;
tryNextDevice ( ) ;
} , 250 ) ;
2016-08-02 00:49:34 -07:00
}
2015-03-24 00:33:19 -07:00
2016-08-02 00:49:34 -07:00
ext . _deviceRemoved = function ( dev ) {
if ( device != dev ) return ;
if ( poller ) poller = clearInterval ( poller ) ;
2015-03-24 00:33:19 -07:00
device = null ;
} ;
2016-08-02 00:49:34 -07:00
ext . _shutdown = function ( ) {
if ( device ) device . close ( ) ;
if ( poller ) poller = clearInterval ( poller ) ;
2015-03-24 00:33:19 -07:00
device = null ;
} ;
2016-08-02 00:49:34 -07:00
ext . _getStatus = function ( ) {
if ( ! device ) return { status : 1 , msg : 'PicoBoard disconnected' } ;
if ( watchdog ) return { status : 1 , msg : 'Probing for PicoBoard' } ;
2015-03-24 00:33:19 -07:00
return { status : 2 , msg : 'PicoBoard connected' } ;
2016-08-02 00:49:34 -07:00
} ;
2015-03-24 00:33:19 -07:00
var descriptor = {
blocks : [
[ 'h' , 'when %m.booleanSensor' , 'whenSensorConnected' , 'button pressed' ] ,
[ 'h' , 'when %m.sensor %m.lessMore %n' , 'whenSensorPass' , 'slider' , '>' , 50 ] ,
[ 'b' , 'sensor %m.booleanSensor?' , 'sensorPressed' , 'button pressed' ] ,
[ 'r' , '%m.sensor sensor value' , 'sensor' , 'slider' ]
] ,
menus : {
booleanSensor : [ 'button pressed' , 'A connected' , 'B connected' , 'C connected' , 'D connected' ] ,
sensor : [ 'slider' , 'light' , 'sound' , 'resistance-A' , 'resistance-B' , 'resistance-C' , 'resistance-D' ] ,
lessMore : [ '>' , '<' ]
} ,
url : '/info/help/studio/tips/ext/PicoBoard/'
} ;
ScratchExtensions . register ( 'PicoBoard' , descriptor , ext , { type : 'serial' } ) ;
} ) ( { } ) ;