├── .gitmodules ├── A4S.jar ├── A4S.java ├── A4S.s2e ├── README.md ├── RXTXcomm.jar ├── build.sh ├── examples └── A4S Example.sb2 ├── librxtxSerial.jnilib ├── librxtxSerial.so ├── librxtxSerial64.so ├── libusb0.dll ├── manifest.mf ├── run.sh └── rxtxSerial.dll /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "processing"] 2 | path = processing 3 | url = https://github.com/firmata/processing.git 4 | -------------------------------------------------------------------------------- /A4S.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damellis/A4S/b584ce4201a7fe60afaca3c4b1b0d53bb7b2ff95/A4S.jar -------------------------------------------------------------------------------- /A4S.java: -------------------------------------------------------------------------------- 1 | // A4S.java 2 | // Copyright (c) MIT Media Laboratory, 2013 3 | // 4 | // Helper app that runs an HTTP server allowing Scratch to communicate with 5 | // Arduino boards running the Firmata firmware (StandardFirmata example). 6 | // 7 | // Note: the Scratch extension mechanism is a work-in-progress and still 8 | // evolving. This code will need updates to work with future version of Scratch. 9 | // 10 | // Based on HTTPExtensionExample by John Maloney. Adapted for Arduino and 11 | // Firmata by David Mellis. 12 | // 13 | // Inspired by Tom Lauwers Finch/Hummingbird server and Conner Hudson's Snap extensions. 14 | 15 | import java.io.*; 16 | import java.net.*; 17 | import java.util.*; 18 | 19 | import gnu.io.CommPort; 20 | import gnu.io.CommPortIdentifier; 21 | import gnu.io.SerialPort; 22 | import gnu.io.SerialPortEvent; 23 | import gnu.io.SerialPortEventListener; 24 | import gnu.io.NoSuchPortException; 25 | import gnu.io.PortInUseException; 26 | import gnu.io.UnsupportedCommOperationException; 27 | 28 | import org.firmata.Firmata; 29 | 30 | public class A4S { 31 | private static int[] firmataPinModes={Firmata.INPUT,Firmata.OUTPUT,Firmata.ANALOG,Firmata.PWM,Firmata.SERVO }; 32 | private static String[] a4sPinModes={"Digital%20Input", "Digital%20Output","Analog%20Input","Analog%20Output%28PWM%29","Servo"}; 33 | 34 | private static final int PORT = 12345; // set to your extension's port number 35 | private static int volume = 8; // replace with your extension's data, if any 36 | 37 | private static InputStream sockIn; 38 | private static OutputStream sockOut; 39 | 40 | private static SerialPort serialPort; 41 | private static Firmata arduino; 42 | 43 | private static SerialReader reader; 44 | 45 | public static class SerialReader implements SerialPortEventListener { 46 | public void serialEvent(SerialPortEvent e) { 47 | try { 48 | while (serialPort.getInputStream().available() > 0) { 49 | int n = serialPort.getInputStream().read(); 50 | //System.out.println(">" + n); 51 | arduino.processInput(n); 52 | } 53 | } catch (IOException err) { 54 | System.err.println(err); 55 | } 56 | } 57 | } 58 | 59 | public static class FirmataWriter implements Firmata.Writer { 60 | public void write(int val) { 61 | try { 62 | //System.out.println("<" + val); 63 | serialPort.getOutputStream().write(val); 64 | } catch (IOException err) { 65 | System.err.println(err); 66 | } 67 | } 68 | } 69 | 70 | public static void main(String[] args) throws IOException { 71 | try { 72 | if (args.length < 1) { 73 | System.err.println("Please specify serial port on command line."); 74 | return; 75 | } 76 | CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(args[0]); 77 | CommPort commPort = portIdentifier.open("A4S",2000); 78 | 79 | if ( commPort instanceof SerialPort ) 80 | { 81 | serialPort = (SerialPort) commPort; 82 | serialPort.setSerialPortParams(57600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE); 83 | 84 | arduino = new Firmata(new FirmataWriter()); 85 | reader = new SerialReader(); 86 | 87 | serialPort.addEventListener(reader); 88 | serialPort.notifyOnDataAvailable(true); 89 | 90 | try { 91 | Thread.sleep(3000); // let bootloader timeout 92 | } catch (InterruptedException e) {} 93 | 94 | arduino.init(); 95 | } 96 | else 97 | { 98 | System.out.println("Error: Only serial ports are handled by this example."); 99 | return; 100 | } 101 | } catch (Exception e) { 102 | System.err.println(e); 103 | return; 104 | } 105 | 106 | InetAddress addr = InetAddress.getLocalHost(); 107 | System.out.println("HTTPExtensionExample helper app started on " + addr.toString()); 108 | 109 | ServerSocket serverSock = new ServerSocket(PORT); 110 | while (true) { 111 | Socket sock = serverSock.accept(); 112 | sockIn = sock.getInputStream(); 113 | sockOut = sock.getOutputStream(); 114 | try { 115 | handleRequest(); 116 | } catch (Exception e) { 117 | e.printStackTrace(System.err); 118 | sendResponse("unknown server error"); 119 | } 120 | sock.close(); 121 | } 122 | } 123 | 124 | private static void handleRequest() throws IOException { 125 | String httpBuf = ""; 126 | int i; 127 | 128 | // read data until the first HTTP header line is complete (i.e. a '\n' is seen) 129 | while ((i = httpBuf.indexOf('\n')) < 0) { 130 | byte[] buf = new byte[5000]; 131 | int bytes_read = sockIn.read(buf, 0, buf.length); 132 | if (bytes_read < 0) { 133 | System.out.println("Socket closed; no HTTP header."); 134 | return; 135 | } 136 | httpBuf += new String(Arrays.copyOf(buf, bytes_read)); 137 | } 138 | 139 | String header = httpBuf.substring(0, i); 140 | if (header.indexOf("GET ") != 0) { 141 | System.out.println("This server only handles HTTP GET requests."); 142 | return; 143 | } 144 | i = header.indexOf("HTTP/1"); 145 | if (i < 0) { 146 | System.out.println("Bad HTTP GET header."); 147 | return; 148 | } 149 | header = header.substring(5, i - 1); 150 | if (header.equals("favicon.ico")) return; // igore browser favicon.ico requests 151 | else if (header.equals("crossdomain.xml")) sendPolicyFile(); 152 | else if (header.length() == 0) doHelp(); 153 | else doCommand(header); 154 | } 155 | 156 | private static void sendPolicyFile() { 157 | // Send a Flash null-teriminated cross-domain policy file. 158 | String policyFile = 159 | "\n" + 160 | " \n" + 161 | "\n\0"; 162 | sendResponse(policyFile); 163 | } 164 | 165 | private static void sendResponse(String s) { 166 | String crlf = "\r\n"; 167 | String httpResponse = "HTTP/1.1 200 OK" + crlf; 168 | httpResponse += "Content-Type: text/html; charset=ISO-8859-1" + crlf; 169 | httpResponse += "Access-Control-Allow-Origin: *" + crlf; 170 | httpResponse += crlf; 171 | httpResponse += s + crlf; 172 | try { 173 | byte[] outBuf = httpResponse.getBytes(); 174 | sockOut.write(outBuf, 0, outBuf.length); 175 | } catch (Exception ignored) { } 176 | } 177 | 178 | private static void doCommand(String cmdAndArgs) { 179 | // Essential: handle commands understood by this server 180 | String response = "okay"; 181 | String[] parts = cmdAndArgs.split("/"); 182 | String cmd = parts[0]; 183 | 184 | //System.out.print(cmdAndArgs); 185 | if (cmd.equals("pinOutput")) { 186 | arduino.pinMode(Integer.parseInt(parts[1]), Firmata.OUTPUT); 187 | } else if (cmd.equals("pinInput")) { 188 | arduino.pinMode(Integer.parseInt(parts[1]), Firmata.INPUT); 189 | } else if (cmd.equals("pinHigh")) { 190 | arduino.digitalWrite(Integer.parseInt(parts[1]), Firmata.HIGH); 191 | } else if (cmd.equals("pinLow")) { 192 | arduino.digitalWrite(Integer.parseInt(parts[1]), Firmata.LOW); 193 | } else if (cmd.equals("pinMode")) { 194 | arduino.pinMode(Integer.parseInt(parts[1]), getFirmataPinMode(parts[2]) ); 195 | } else if (cmd.equals("digitalWrite")) { 196 | arduino.digitalWrite(Integer.parseInt(parts[1]), "high".equals(parts[2]) ? Firmata.HIGH : Firmata.LOW); 197 | } else if (cmd.equals("analogWrite")) { 198 | arduino.analogWrite(Integer.parseInt(parts[1]), Integer.parseInt(parts[2])); 199 | } else if (cmd.equals("servoWrite")) { 200 | arduino.servoWrite(Integer.parseInt(parts[1]), Integer.parseInt(parts[2])); 201 | } else if (cmd.equals("poll")) { 202 | // set response to a collection of sensor, value pairs, one pair per line 203 | // in this example there is only one sensor, "volume" 204 | //response = "volume " + volume + "\n"; 205 | response = ""; 206 | for (int i = 2; i <= 13; i++) { 207 | response += "digitalRead/" + i + " " + (arduino.digitalRead(i) == Firmata.HIGH ? "true" : "false") + "\n"; 208 | } 209 | for (int i = 0; i <= 5; i++) { 210 | response += "analogRead/" + i + " " + (arduino.analogRead(i)) + "\n"; 211 | } 212 | } else { 213 | response = "unknown command: " + cmd; 214 | } 215 | //System.out.println(" " + response); 216 | sendResponse(response); 217 | } 218 | private static int getFirmataPinMode(String a4sPinMode){ 219 | int idx=0; 220 | while (idx < a4sPinModes.length-1 && (! a4sPinMode.equals(a4sPinModes[idx]))) idx++; 221 | if (! a4sPinMode.equals(a4sPinModes[idx]) ) idx=0; 222 | return firmataPinModes[idx]; 223 | } 224 | private static void doHelp() { 225 | // Optional: return a list of commands understood by this server 226 | String help = "HTTP Extension Example Server

"; 227 | sendResponse(help); 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /A4S.s2e: -------------------------------------------------------------------------------- 1 | { "extensionName": "A4S (Arduino For Scratch)", 2 | "extensionPort": 12345, 3 | "blockSpecs": [ 4 | // [" ", "set pin %n high", "pinHigh", 13], 5 | // [" ", "set pin %n low", "pinLow", 13], 6 | // [" ", "set pin %n as output", "pinOutput", 13], 7 | // [" ", "set pin %n as input", "pinInput", 13], 8 | 9 | [" ", "set pin %n as %m.mode", "pinMode", 2, "Digital Input"], 10 | [" ", "digital write pin %n %m.highLow", "digitalWrite", 13, "high"], 11 | [" ", "analog write pin %n value %n", "analogWrite", 3, 255], 12 | [" ", "servo write pin %n degrees %n", "servoWrite", 5, 180], 13 | ["b", "digital read pin %n", "digitalRead", 2], 14 | ["r", "analog read pin %n", "analogRead", 0], 15 | ], 16 | "menus": { 17 | "mode": ["Digital Input", "Digital Output","Analog Input","Analog Output(PWM)","Servo"], 18 | "highLow": ["high", "low"], 19 | }, 20 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A4S (Arduino For Scratch) 2 | 3 | A4S is an experimental extension for [Scratch 2](http://scratch.mit.edu) that allows it to communicate with [Arduino](http://www.arduino.cc) boards using [Firmata](http://firmata.org/). It consists of a Java server that Scratch connects to over HTTP and which communicates with an Arduino board over (USB) serial. A4S is being developed by David Mellis, based on documentation and code from the Scratch team. For updates, see: 4 | 5 |

**This extension has been superseded by the new ScratchX.org site and extension system. See [the Arduino extension for ScratchX.org](http://khanning.github.io/scratch-arduino-extension/).**




6 | 7 | ## Prerequisites 8 | 9 | ### Ubuntu 10 | Install the *librxtx-java* library : `sudo apt-get install librxtx-java`. 11 | 12 | ## Instructions 13 | 14 | 1. Install the [Scratch 2 offline editor](http://scratch.mit.edu/scratch2download/). 15 | 2. Install the [Arduino software](http://arduino.cc/en/Main/Software). Instructions: [Windows](http://arduino.cc/en/Guide/Windows), [Mac OS X](http://arduino.cc/en/Guide/MacOSX). 16 | 3. Upload the StandardFirmata firmware to your Arduino board. (It's in "Examples > Firmata".) 17 | 4. [Download the A4S code](https://github.com/damellis/A4S/archive/master.zip) from GitHub and unzip it. 18 | 5. Launch the A4S server using the "run.sh" script on the command line. Pass the name of the serial port corresponding to your Arduino board as the first argument to the script, e.g. "./run.sh /dev/tty.usbmodem411". You should see a message like: 19 | 20 | Stable Library 21 | ========================================= 22 | Native lib Version = RXTX-2.1-7 23 | Java lib Version = RXTX-2.1-7 24 | HTTPExtensionExample helper app started on Mellis.local/18.189.9.217 25 | 6. Run the Scratch 2 offline editor. 26 | 7. While holding the shift key on your keyboard, click on the "File" menu in Scratch. You should see "Import Experimental Extension" at the bottom of the menu. Click on it. 27 | 8. Navigate to the directory containing A4S and select the A4S.s2e file. 28 | 9. You should see the A4S extension and blocks appear in the "More Blocks" category in the Scratch editor. If the A4S server is running, there will be a green dot next to the "A4S" title. 29 | -------------------------------------------------------------------------------- /RXTXcomm.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damellis/A4S/b584ce4201a7fe60afaca3c4b1b0d53bb7b2ff95/RXTXcomm.jar -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | javac -classpath RXTXcomm.jar A4S.java processing/src/Firmata.java 3 | mkdir -p org/firmata 4 | cp processing/src/*.class org/firmata/ 5 | jar -cfm A4S.jar manifest.mf *.class org/firmata/*.class 6 | rm *.class org/firmata/*.class 7 | -------------------------------------------------------------------------------- /examples/A4S Example.sb2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damellis/A4S/b584ce4201a7fe60afaca3c4b1b0d53bb7b2ff95/examples/A4S Example.sb2 -------------------------------------------------------------------------------- /librxtxSerial.jnilib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damellis/A4S/b584ce4201a7fe60afaca3c4b1b0d53bb7b2ff95/librxtxSerial.jnilib -------------------------------------------------------------------------------- /librxtxSerial.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damellis/A4S/b584ce4201a7fe60afaca3c4b1b0d53bb7b2ff95/librxtxSerial.so -------------------------------------------------------------------------------- /librxtxSerial64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damellis/A4S/b584ce4201a7fe60afaca3c4b1b0d53bb7b2ff95/librxtxSerial64.so -------------------------------------------------------------------------------- /libusb0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damellis/A4S/b584ce4201a7fe60afaca3c4b1b0d53bb7b2ff95/libusb0.dll -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Main-Class: A4S 2 | Class-Path: RXTXcomm.jar A4S.jar 3 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | java -d32 -jar A4S.jar $@ 3 | -------------------------------------------------------------------------------- /rxtxSerial.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damellis/A4S/b584ce4201a7fe60afaca3c4b1b0d53bb7b2ff95/rxtxSerial.dll --------------------------------------------------------------------------------