Introduction:
In this exercise, youwill complete your Wireless Sensor Application by enabling the Wireless Networking capabilities of the Sun SPOT and connecting your two applications wirelessly. At the end of this exercise, bending the Bend Sensor should result in a corresponding movement of the servo on the other end.
To enable the Radio Connection you will be using the BendySender and ServoReceiver classes in their respective projects. All of the network code for these two classes has already been implemented, and you will be enabling them in order to allow the network connections.
Steps to follow:
Editiing the Manifest Files
1 /* 2 * Copyright (c) 2007 Sun Microsystems, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person 5 * obtaining a copy of this software and associated documentation 6 * files (the "Software"), to deal in the Software without 7 * restriction, including without limitation the rights to use, copy, 8 * modify, merge, publish, distribute, sublicense, and/or sell copies 9 * of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 26 package org.sunspotworld; 27 28 import com.sun.spot.sensorboard.EDemoBoard; 29 import com.sun.spot.sensorboard.peripheral.ISwitch; 30 import com.sun.spot.sensorboard.peripheral.ITriColorLED; 31 import com.sun.spot.io.j2me.radiostream.*; 32 import com.sun.spot.io.j2me.radiogram.*; 33 import com.sun.spot.sensorboard.io.IScalarInput; 34 import com.sun.spot.sensorboard.peripheral.ISwitchListener; 35 import com.sun.spot.util.*; 36 37 import java.io.*; 38 import javax.microedition.io.*; 39 import javax.microedition.midlet.MIDlet; 40 import javax.microedition.midlet.MIDletStateChangeException; 41 42 /** 43 * The startApp method of this class is called by the VM to start the 44 * application. 45 * 46 * The manifest specifies this class as MIDlet-1, which means it will 47 * be selected for execution. 48 * 49 * This is the main class for the Bend Sensor application and 'controls' 50 * the reading of sensor data and sending of sensor data to the remote 51 * Sun SPOT. 52 */ 53 public class Bendy extends MIDlet implements ISwitchListener { 54 55 private ITriColorLED[] leds = EDemoBoard.getInstance().getLEDs(); 56 private static final int SAMPLE_SIZE = 150; 57 private int bendMin = 1000; // any high value will do. 58 private int bendCenter = 0; // any low value will do 59 private int bendMax = 0; // any low value will do 60 private IScalarInput bend = EDemoBoard.getInstance().getScalarInputs()[EDemoBoard.A0]; // the bend sensor is » connected to pin A0 61 private ISwitch sw1 = EDemoBoard.getInstance().getSwitches()[EDemoBoard.SW1]; 62 private ISwitch sw2 = EDemoBoard.getInstance().getSwitches()[EDemoBoard.SW2]; 63 // private BendySender nw; 64 65 66 /** 67 * This is the main method called to start the Sun SPOT Application 68 * @throws javax.microedition.midlet.MIDletStateChangeException Thrown when the state of the MIDlet is changed 69 */ 70 protected void startApp() throws MIDletStateChangeException { 71 new BootloaderListener().start(); // monitor the USB (if connected) and recognize commands from host 72 // nw = new BendySender(); 73 calibrate(); 74 sw2.addISwitchListener(this); 75 readSensor(); // this never returns. 76 } 77 78 /** 79 * Reads the data from the Sensor itself. 80 * 81 * This method never returns. 82 */ 83 private void readSensor(){ 84 int lastVal = 0; 85 int bendInt = 0; 86 leds[7].setRGB(0, 250, 0); 87 while (true) { 88 try { 89 bendInt = bend.getValue(); 90 } catch (IOException ex) { 91 ex.printStackTrace(); 92 } 93 if ((bendInt > lastVal + 15) || (bendInt < lastVal - 15)) { 94 lastVal = bendInt; 95 String msg = String.valueOf(bendInt); 96 System.out.println("Read Sensor Value: " + msg); 97 // try { 98 // nw.send(msg); 99 // } catch (IOException ex) { 100 // ex.printStackTrace(); 101 // } 102 } 103 leds[7].setOn(!leds[7].isOn()); 104 Utils.sleep(50); 105 } 106 } 107 108 109 110 /** 111 * Calibrate (or re-calibrate) the sensor based on 112 * user input. 113 */ 114 protected void calibrate() { 115 leds[7].setOff(); 116 setBendMin(1000); 117 setBendCenter(0); 118 setBendMax(0); 119 System.out.println("Do not touch bend sensor while red light is flashing ..."); 120 Utils.sleep(1000); 121 leds[0].setRGB(250, 0, 0); // set color to moderate red 122 int z = 0; 123 /* 124 * Collect a large sample size and average it to find the 'neutral' position 125 */ 126 while (z < SAMPLE_SIZE) { 127 try { 128 setBendCenter(getBendCenter() + bend.getValue()); 129 z++; // only collect successful reads 130 } catch (IOException ex) { 131 ex.printStackTrace(); 132 } 133 leds[0].setOn(!leds[0].isOn()); // Blink LED 134 Utils.sleep(50); 135 } 136 setBendCenter(getBendCenter() / SAMPLE_SIZE); // average over the sample size 137 System.out.println("Center Average: " + getBendCenter() + "(over " + SAMPLE_SIZE + " readings)"); 138 System.out.println("Calibrate bend Sensor. Bend in both directions several times. Click Switch one when done."); 139 leds[0].setRGB(0, 0, 250); // set to blue 140 while (sw1.isOpen()) { // done when switch is pressed 141 try { 142 int bendVal = bend.getValue(); 143 if (bendVal < getBendMin()) { 144 setBendMin(bendVal); 145 } 146 if (bendVal > getBendMax()) { 147 setBendMax(bendVal); 148 } 149 System.out.println("Bend: " + bendVal + " Min: " + getBendMin() + " Max: " + getBendMax()); 150 leds[0].setOn(!leds[0].isOn()); // Blink LED 151 Utils.sleep(50); 152 } catch (IOException ex) { 153 ex.printStackTrace(); 154 } 155 } 156 System.out.println(" Setting Min: " + getBendMin() + " Center: " + getBendCenter() + " Max: " + » getBendMax()); 157 leds[0].setOff(); 158 // nw.connect(); 159 160 } 161 162 protected void pauseApp() { 163 // This is not currently called by the Squawk VM 164 } 165 166 /** 167 * Called if the MIDlet is terminated by the system. 168 * I.e. if startApp throws any exception other than MIDletStateChangeException, 169 * if the isolate running the MIDlet is killed with Isolate.exit(), or 170 * if VM.stopVM() is called. 171 * 172 * It is not called if MIDlet.notifyDestroyed() was called. 173 * @param unconditional If true when this method is called, the MIDlet must 174 * cleanup and release all resources. If false the MIDlet may throw 175 * MIDletStateChangeException to indicate it does not want to be destroyed 176 * at this time. 177 * @throws javax.microedition.midlet.MIDletStateChangeException 178 */ 179 protected void destroyApp(boolean unconditional) throws MIDletStateChangeException { 180 for (int i = 0; i < 8; i++) { 181 leds[i].setOff(); 182 } 183 } 184 185 /** 186 * Return the currently-set minimum value for the bend sensor 187 * @return Sensor minimum value 188 */ 189 public int getBendMin() { 190 return bendMin; 191 } 192 193 /** 194 * Set the minimum value read from the Bend Sensor during configuration 195 * @param bendMin The minimum value read from the sensor 196 */ 197 public void setBendMin(int bendMin) { 198 this.bendMin = bendMin; 199 } 200 201 /** 202 * Return the currently stored 'center' point of the bend sensor 203 * as calculated 204 * @return The stored center-point of the sensor 205 */ 206 public int getBendCenter() { 207 return bendCenter; 208 } 209 210 /** 211 * Store the current 'center' point of the bend sensor 212 * as calculated 213 * @param bendCenter The calculated center point 214 */ 215 public void setBendCenter(int bendCenter) { 216 this.bendCenter = bendCenter; 217 } 218 219 /** 220 * Return the currently stored 'max' point of the bend sensor 221 * as calculated 222 * @return maximum possible sensor value 223 */ 224 public int getBendMax() { 225 return bendMax; 226 } 227 228 /** 229 * set the current 'maximum' point of the bend sensor 230 * as calculated 231 * @param bendMax calculated maximum sensor value 232 */ 233 public void setBendMax(int bendMax) { 234 this.bendMax = bendMax; 235 } 236 237 /* 238 * We watch Switch 2 for a press, and if pressed, recalibrate the bend sensor 239 * and send the new calibration data to the servo. 240 */ 241 /** 242 * Monitor Switch for a press. 243 * When pressed, re-calibrate the bend sensor and 244 * send recalibration message to the remote 245 * Sun SPOT. 246 * @param sw The switch to watch 247 */ 248 public void switchPressed(ISwitch sw) { 249 int switchNum = (sw == sw1) ? 1 : 2; 250 System.out.println("Switch " + switchNum + " closed."); 251 calibrate(); 252 } 253 254 public void switchReleased(ISwitch arg0) { 255 } 256 }
1 /* 2 * Copyright (c) 2007 Sun Microsystems, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person 5 * obtaining a copy of this software and associated documentation 6 * files (the "Software"), to deal in the Software without 7 * restriction, including without limitation the rights to use, copy, 8 * modify, merge, publish, distribute, sublicense, and/or sell copies 9 * of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 package org.sunspotworld; 26 27 import com.sun.spot.sensorboard.EDemoBoard; 28 import com.sun.spot.sensorboard.peripheral.ISwitch; 29 import com.sun.spot.sensorboard.peripheral.ITriColorLED; 30 import com.sun.spot.io.j2me.radiostream.*; 31 import com.sun.spot.io.j2me.radiogram.*; 32 import com.sun.spot.sensorboard.peripheral.ISwitchListener; 33 import com.sun.spot.sensorboard.peripheral.Servo; 34 import com.sun.spot.util.*; 35 import java.io.*; 36 import javax.microedition.io.*; 37 import javax.microedition.midlet.MIDlet; 38 import javax.microedition.midlet.MIDletStateChangeException; 39 40 /** 41 * The startApp method of this class is called by the VM to start the 42 * application. 43 * 44 * The manifest specifies this class as MIDlet-1, which means it will 45 * be selected for execution. 46 * 47 * This class controls the actual Servo, and reads from another class which 48 * controls the Radio. 49 */ 50 public class ServoMover extends MIDlet implements ISwitchListener{ 51 52 private ITriColorLED[] leds = EDemoBoard.getInstance().getLEDs(); 53 private static final int CENTER = 1500; 54 private static final int MAX = 2500; 55 private static final int MIN = 500; 56 private ISwitch[] switches = EDemoBoard.getInstance().getSwitches(); 57 private ServoReceiver snw; 58 private Servo serv; 59 60 /** 61 * This is the main method called to start the MIDlet application. 62 * @throws javax.microedition.midlet.MIDletStateChangeException Throw exception if the MIDLet fails 63 */ 64 protected void startApp() throws MIDletStateChangeException { 65 new BootloaderListener().start(); // monitor the USB (if connected) and recognize commands from host 66 switches[0].addISwitchListener(this); 67 switches[1].addISwitchListener(this); 68 serv = new Servo(EDemoBoard.getInstance().getOutputPins()[EDemoBoard.H0]); 69 serv.setValue(CENTER); 70 Utils.sleep(500); 71 while (serv.getValue() < MAX) { 72 int cur = serv.getValue(); 73 serv.setValue(cur + 100); 74 Utils.sleep(250); 75 } 76 while (serv.getValue() > MIN) { 77 int cur = serv.getValue(); 78 serv.setValue(cur - 100); 79 Utils.sleep(250); 80 } 81 // snw = new ServoReceiver(this); 82 // System.out.println("Waiting for calibration data..."); 83 // snw.connect(); 84 // Thread t = new Thread(snw); 85 // t.start(); 86 } 87 88 /** 89 * Move the servo the specified amount. 90 * @param bendValue Value of the sensor read. Must be converted to a servo-position before use. 91 */ 92 public void moveServo(int bendValue) { 93 /* 94 * the formula is, roughly: 1000 * cube-root[(value-min)/60] + 500, but since we 95 * only do *integer* cube roots, we make the value *really* big first (10^3), then 96 * reduce it again ( /10 ) later. It's not perfect, but it's pretty close 97 */ 98 int sMove = (icbrt((int) ((((double) bendValue - (double) snw.getMin()) / 60) * 1000000)) * 10) + 500; 99 System.out.println("Read sensor value: " + bendValue + " Moving Servo to: " + sMove); 100 serv.setValue(sMove); 101 102 } 103 104 /** 105 * Handler called when a watched switch is pressed. 106 * @param sw the switch that was pressed 107 */ 108 public void switchPressed(ISwitch sw) { 109 int switchNum = (sw == switches[0]) ? 1 : 2; 110 System.out.println("Switch " + switchNum + " closed."); 111 manualMove(switchNum); 112 } 113 114 /** 115 * Handler called when a switch is released 116 * @param sw the switch that was released 117 */ 118 public void switchReleased(ISwitch sw) { 119 int switchNum = (sw == switches[0]) ? 1 : 2; 120 System.out.println("Switch " + switchNum + " opened."); 121 manualMove(switchNum); 122 } 123 124 /** 125 * Move a servo based on a value for direction 126 * @param dir 1 or 2, corresponds to switch 1 or 2 to move the servo 127 */ 128 private void manualMove (int dir) { 129 final int move = 25; 130 int servPos = serv.getValue(); 131 if(dir == 1){ 132 if(servPos - move > MIN){ 133 serv.setValue(servPos - move); 134 } else { 135 serv.setValue(MIN); 136 } 137 } else { 138 if(servPos + move < MAX ){ 139 serv.setValue(servPos + move); 140 } else { 141 serv.setValue(MAX); 142 } 143 } 144 } 145 146 147 148 /** 149 * This is ugly code, found on the internet, for doing, basically, integer cube roots. 150 * nasty stuff, but it comes astonishingly close, so it's 'good enough' 151 * Squawk doesn't have built-in cube-root, so this is our only hope. 152 * @return Integer cube-root 153 * @param x The integer to reduce to cube-root 154 */ 155 private int icbrt(int x) { 156 int s = 30; 157 int y, b; 158 y = 0; 159 while (s >= 0) { 160 y = 2 * y; 161 b = (3 * y * (y + 1) + 1) << s; 162 s = s - 3; 163 if (x >= b) { 164 x = x - b; 165 y = y + 1; 166 } 167 } 168 return y; 169 } 170 171 protected void pauseApp() { 172 // This is not currently called by the Squawk VM 173 } 174 175 /** 176 * Called if the MIDlet is terminated by the system. 177 * I.e. if startApp throws any exception other than MIDletStateChangeException, 178 * if the isolate running the MIDlet is killed with Isolate.exit(), or 179 * if VM.stopVM() is called. 180 * 181 * It is not called if MIDlet.notifyDestroyed() was called. 182 * @param unconditional If true when this method is called, the MIDlet must 183 * cleanup and release all resources. If false the MIDlet may throw 184 * MIDletStateChangeException to indicate it does not want to be destroyed 185 * at this time. 186 * @throws javax.microedition.midlet.MIDletStateChangeException Thrown when the state of the MIDLet is changed. 187 */ 188 protected void destroyApp(boolean unconditional) throws MIDletStateChangeException { 189 for (int i = 0; i < 8; i++) { 190 leds[i].setOff(); 191 } 192 } 193 194 195 196 }
Your complete hardware setup should look like this:
Summary:
You should now have a fully functioning, networked Sun SPOT Wireless sensor application which reads data from a non-linear, analog Bend Sensor and transmits those readings over a wireless connection to a remote Sun SPOT which translates those values into linear values for a Servo and moves the servo.