It is about 50x50cm with 20cm wheels and uses PiBorg Diable DC motor controller.
It has the same control system as the Small Rover but it do not use the internal DC motor controller.
Below is the HTML and python code, it is not so much because the Steelsquid Kiss OS gives a lot of functionality automatically. All this code is already in Steelsquid Kiss OS you can enable it by executing:
steelsquid module kiss_rover on small
But for it to work you need this:
See the newest file on GitHub
kiss_rover.py #!/usr/bin/python -OO '''. Fuctionality for my rover controller Also see rover.html (called from utils.html) For this to start on boot you need to enable it, you can do it like this: Command line: steelsquid module kiss_expand on Python: steelsquid_kiss_global.module_status("kiss_expand", True) Syncrinization program: Press E and then enter You can omit all the undelaying methods and classes and this module will just be imported... Varibale is_started Is this module started This is set by the system automatically. If a method named enable exist: When this module is enabled what needs to be done (execute: steelsquid module XXX on) Maybe you need create some files or enable other stuff. If a method named disable exist: When this module is disabled what needs to be done (execute: steelsquid module XXX off) Maybe you need remove some files or disable other stuff. If Class with name SETTINGS exist: The system will try to load settings with the same name as all variables in the class SETTINGS. If the variable value is Boolean: steelsquid_utils.get_flag("variable_name") If the variable value is Integer, Float, String: steelsquid_utils.get_parameter("variable_name") If the variable value is Array []: steelsquid_utils.get_list("variable_name") The variable value will also be used as default value if the paramater or list not is found When the system shutdowen the value of the variable will also be saved to disk EX: class SETTINGS(object): this_is_a_flag = False this_is_a_parameter = "a_default_value" this_is_a_list = [] System try to read: steelsquid_utils.get_flag("this_is_a_flag") System try to read: steelsquid_utils.get_parameter("this_is_a_parameter", "a_default_value") System try to read: steelsquid_utils.get_list("this_is_a_list", []) If you want to disable save and read the settings from disk add a variable like this. This is usefull under development if you wan to test different values when you restart the module, otherwise the value from the first execution to be used ... _persistent_off = True To sum up: Variables in class SETTINGS that has value: Boolean, Array, Integer, Float, String will be persistent. If Class with name SYSTEM has this staticmethods on_start() exist it will be executed when system starts (boot) on_stop() exist it will be executed when system stops (shutdown) on_network(status, wired, wifi_ssid, wifi, wan) exist it will be execute on network up or down on_vpn(status, name, ip) This will fire when a VPN connection is enabled/disabled. on_bluetooth(status) exist it will be execute on bluetooth enabled on_mount(type_of_mount, remote, local) This will fire when USB, Samba(windows share) or SSH is mounted. on_umount(type_of_mount, remote, local) This will fire when USB, Samba(windows share) or SSH is unmounted. on_event_data(key, value) exist it will execute when data is changed with steelsquid_kiss_global.set_event_data(key, value) If Class with name LOOP Every static method with no inparameters will execute over and over again untill it return None or -1 If it return a number larger than 0 it will sleep for that number of seconds before execute again. If it return 0 it will not not sleep, will execute again immediately. Every method will execute in its own thread Class with name EVENTS Create staticmethods in this class to listen for asynchronous events. Example: If you have a method like this: @staticmethod def this_is_a_event(a_parameter, another_parameter): print a_parameter+":"+another_parameter Then if a thread somewhere in the system execute this: steelsquid_kiss_global.broadcast_event("this_is_a_event", ("para1", "para2",)) The method def this_is_a_event will execute asynchronous Class with name WEB: Methods in this class will be executed by the webserver if module is activated and the webserver is enabled If is a GET it will return files and if it is a POST it executed commands. It is meant to be used as follows. 1. Make a call from the browser (GET) and a html page is returned back. 2. This html page then make AJAX (POST) call to the server to retrieve or update data. 3. The data sent to and from the server can just be a simple list of strings. See steelsquid_http_server.py for more examples how it work Class with name SOCKET: Methods in this class will be executed by the socket connection if module is activated and the socket connection is enabled A simple class that i use to sen async socket command to and from client/server. A request can be made from server to client or from client to server See steelsquid_connection.py and steelsquid_socket_connection.py - on_connect(remote_address): When a connection is enabled - on_disconnect(error_message): When a connection is lost If this is a PIIO board Methods in this class will be executed by the system if module is enabled and this is a PIIO board Enebale this module like this: steelsquid piio-on on_voltage_change(voltage) Will fire when in voltage to the PIIO board i changed on_low_bat(voltage) exist it will execute when voltage is to low. on_button(button_nr) exist it will execute when button 1 to 6 is clicken on the PIIO board on_button_info() exist it will execute when info button clicken on the PIIO board on_switch(dip_nr, status) exist it will execute when switch 1 to 6 is is changed on the PIIO board on_movement(x, y, z) will execute if Geeetech MPU-6050 is connected and the device is moved. on_rotation(x, y) will execute if Geeetech MPU-6050 is connected and the device is tilted. The class with name GLOBAL Put global staticmethods in this class, methods you use from different part of the system. Maybe the same methods is used from the WEB, SOCKET or other part, then put that method her. It is not necessary to put it her, you can also put it direcly in the module or use a nother name (but i think it is kind of nice to have it inside this class) @organization: Steelsquid @author: Andreas Nilsson @contact: steelsquid@gmail.com @license: GNU Lesser General Public License v2.1 @change: 2014-12-26 Created ''' import steelsquid_utils import steelsquid_pi import steelsquid_piio import steelsquid_kiss_global import time import datetime # Is this module started # This is set by the system automatically. is_started = False def enable(argument=None): ''' When this module is enabled what needs to be done (execute: steelsquid module XXX on) Maybe you need create some files or enable other stuff. argument: Send data to the enable or disable method in the module Usually a string to tell the start/stop something ''' #Clear any saved settings for this module steelsquid_kiss_global.clear_modules_settings("kiss_rover") # Get what tyoe of rover this is from the start argument if argument!=None and argument=="large": steelsquid_utils.set_parameter("rover_type", "large") steelsquid_utils.set_parameter("servo_position_start", "70") steelsquid_utils.set_parameter("servo_position_max", "160") steelsquid_utils.set_parameter("servo_position_min", "10") steelsquid_utils.set_parameter("motor_forward_max", "1000") steelsquid_utils.set_parameter("motor_backward_max", "-1000") steelsquid_utils.set_parameter("motor_forward_start", "75") steelsquid_utils.set_parameter("motor_backward_start", "-75") elif argument!=None and argument=="mini": steelsquid_utils.set_parameter("rover_type", "mini") steelsquid_utils.set_parameter("servo_position_start", "85") steelsquid_utils.set_parameter("servo_position_max", "140") steelsquid_utils.set_parameter("servo_position_min", "30") steelsquid_utils.set_parameter("motor_forward_max", "800") steelsquid_utils.set_parameter("motor_backward_max", "-800") steelsquid_utils.set_parameter("motor_forward_start", "170") steelsquid_utils.set_parameter("motor_backward_start", "-170") else: steelsquid_utils.set_parameter("rover_type", "small") steelsquid_utils.set_parameter("servo_position_start", "210") steelsquid_utils.set_parameter("servo_position_max", "230") steelsquid_utils.set_parameter("servo_position_min", "80") steelsquid_utils.set_parameter("motor_forward_max", "1023") steelsquid_utils.set_parameter("motor_backward_max", "-1023") steelsquid_utils.set_parameter("motor_forward_start", "200") steelsquid_utils.set_parameter("motor_backward_start", "-200") # Enable the PIIO board if not steelsquid_kiss_global.is_module_enabled("kiss_piio") and not steelsquid_kiss_global.stream(): steelsquid_kiss_global.module_status("kiss_piio", True, restart=False) # Not trigger reboot elif not steelsquid_kiss_global.is_module_enabled("kiss_piio") and steelsquid_kiss_global.stream(): steelsquid_kiss_global.module_status("kiss_piio", True, restart=True) # Trigger reboot # Enable PI camera streaming (if necessary) if not steelsquid_kiss_global.stream() == "pi": steelsquid_kiss_global.stream_pi() #Will trigger reboot def disable(argument=None): ''' When this module is disabled what needs to be done (execute: steelsquid module XXX off) Maybe you need remove some files or disable other stuff. argument: Send data to the enable or disable method in the module Usually a string to tell the start/stop something ''' # Disable the PIIO board and PI camera streaming (if necessary) if steelsquid_kiss_global.is_module_enabled("kiss_piio") and steelsquid_kiss_global.stream(): steelsquid_kiss_global.module_status("kiss_piio", False, restart=False) # Not trigger reboot elif steelsquid_kiss_global.is_module_enabled("kiss_piio") and steelsquid_kiss_global.stream(): steelsquid_kiss_global.module_status("kiss_piio", False, restart=True) # Trigger reboot if steelsquid_kiss_global.stream() == "pi": steelsquid_kiss_global.stream_off() #Will trigger reboot class SETTINGS(object): ''' The system will try to load settings with the same name as all variables in the class SETTINGS. If the variable value is Boolean: steelsquid_utils.get_flag("variable_name") If the variable value is Integer, Float, String: steelsquid_utils.get_parameter("variable_name") If the variable value is Array []: steelsquid_utils.get_list("variable_name") The variable value will also be used as default value if the paramater or list not is found When the system shutdowen the value of the variable will also be saved to disk EX: this_is_a_flag = False this_is_a_parameter = "a_default_value" this_is_a_list = [] System try to read: steelsquid_utils.get_flag("this_is_a_flag") System try to read: steelsquid_utils.get_parameter("this_is_a_parameter", "a_default_value") System try to read: steelsquid_utils.get_list("this_is_a_list", []) If you want to disable save and read the settings from disk add a variable like this. This is usefull under development if you wan to test different values when you restart the module, otherwise the value from the first execution to be used ... _persistent_off = True To sum up: Variables in class SETTINGS that has value: Boolean, Array, Integer, Float, String will be will be persistent. ''' # This will tell the system not to save and read the settings from disk _persistent_off = True # What type of rover is this. This is set by the enable module (Small or large, default mini) rover_type = "mini" # The lamp is connected to this POWER PIN lamp_power_pin = 1 # The front edge detection sensor is connected to this GPIO front_edge_gpio = 14 # The front edge detection sensor is connected to this GPIO left_edge_gpio = 15 # The front edge detection sensor is connected to this GPIO right_edge_gpio = 13 # The front edge detection sensor is connected to this GPIO back_edge_gpio = 16 # The distance sensor gpios front_echo_gpio = 1 front_trig_gpio = 17 front_right_echo_gpio = 2 front_right_trig_gpio = 18 right_echo_gpio = 3 right_trig_gpio = 19 back_right_echo_gpio = 4 back_right_trig_gpio = 20 back_echo_gpio = 5 back_trig_gpio = 21 back_left_echo_gpio = 6 back_left_trig_gpio = 22 left_echo_gpio = 7 left_trig_gpio = 9 front_left_echo_gpio = 8 front_left_trig_gpio = 10 # Number of seconds until drive stop if no commands from client (connection lost, stop the rover) max_drive_delta = 1 # When system start move servo here servo_position_start = 85 # Max Servo position servo_position_max = 140 # Min Servo position servo_position_min = 30 # Motor max forward motor_forward_max = 800 # Motor max backward motor_backward_max = -800 # start with this value when drive forward (if lower than this the motor do not turn) motor_forward_start = 170 # start with this value when drive backward (if lower than this the motor do not turn) motor_backward_start = -170 class SYSTEM(object): ''' Methods in this class will be executed by the system if module is enabled on_start() exist it will be executed when system starts (boot) on_stop() exist it will be executed when system stops (shutdown) on_network(status, wired, wifi_ssid, wifi, wan) exist it will be execute on network up or down on_vpn(status, name, ip) This will fire when a VPN connection is enabled/disabled. on_bluetooth(status) exist it will be execute on bluetooth enabled on_mount(type_of_mount, remote, local) This will fire when USB, Samba(windows share) or SSH is mounted. on_umount(type_of_mount, remote, local) This will fire when USB, Samba(windows share) or SSH is unmounted. on_event_data(key, value) exist it will execute when data is changed with steelsquid_kiss_global.set_event_data(key, value) ''' @staticmethod def on_start(): ''' This will execute when system starts Do not execute long running stuff here, do it in on_loop... ''' steelsquid_utils.shout("Steelsquid Rover enabled ("+SETTINGS.rover_type+")") # Set servo start position WEB.servo_position = SETTINGS.servo_position_start # Move the sevo to start position steelsquid_piio.servo(1, WEB.servo_position) # The mini rover has edge detection sensors if SETTINGS.rover_type=="mini": steelsquid_piio.gpio_event(SETTINGS.front_edge_gpio, SYSTEM.on_edge, bouncetime_ms=0, resistor=steelsquid_piio.PULL_NONE) steelsquid_piio.gpio_event(SETTINGS.left_edge_gpio, SYSTEM.on_edge, bouncetime_ms=0, resistor=steelsquid_piio.PULL_NONE) steelsquid_piio.gpio_event(SETTINGS.right_edge_gpio, SYSTEM.on_edge, bouncetime_ms=0, resistor=steelsquid_piio.PULL_NONE) steelsquid_piio.gpio_event(SETTINGS.back_edge_gpio, SYSTEM.on_edge, bouncetime_ms=0, resistor=steelsquid_piio.PULL_NONE) @staticmethod def on_stop(): ''' This will execute when system stops Do not execute long running stuff here ''' pass @staticmethod def on_edge(gpio, state): ''' On edge detection If front edge detect: state=True ''' # Light different PIIO leds if gpio == SETTINGS.front_edge_gpio: steelsquid_piio.led(4, state) elif gpio == SETTINGS.left_edge_gpio: steelsquid_piio.led(3, state) elif gpio == SETTINGS.right_edge_gpio: steelsquid_piio.led(2, state) elif gpio == SETTINGS.back_edge_gpio: steelsquid_piio.led(1, state) class LOOP(object): ''' Every static method with no inparameters will execute over and over again untill it return None or -1 If it return a number larger than 0 it will sleep for that number of seconds before execute again. If it return 0 it will not not sleep, will execute again immediately. Every method will execute in its own thread ''' # Last distance read front_distance=999 front_right_distance=999 right_distance=999 back_right_distance=999 back_distance=999 back_left_distance=999 left_distance=999 front_left_distance=999 @staticmethod def on_loop(): ''' Execute every 0.5 second ''' # If more than "configurable" second since last drive command stop the drive (connection may be lost) drive_delta = datetime.datetime.now() - WEB.last_drive_command if drive_delta.total_seconds()>1: steelsquid_piio.motor(0, 0) # Read front sensor distance LOOP.front_distance = steelsquid_piio.hcsr04_distance(SETTINGS.front_trig_gpio, SETTINGS.front_echo_gpio) LOOP.front_right_distance = steelsquid_piio.hcsr04_distance(SETTINGS.front_right_trig_gpio, SETTINGS.front_right_echo_gpio) LOOP.right_distance = steelsquid_piio.hcsr04_distance(SETTINGS.right_trig_gpio, SETTINGS.right_echo_gpio) LOOP.back_right_distance = steelsquid_piio.hcsr04_distance(SETTINGS.back_right_trig_gpio, SETTINGS.back_right_echo_gpio) LOOP.back_distance = steelsquid_piio.hcsr04_distance(SETTINGS.back_trig_gpio, SETTINGS.back_echo_gpio) LOOP.back_left_distance = steelsquid_piio.hcsr04_distance(SETTINGS.back_left_trig_gpio, SETTINGS.back_left_echo_gpio) LOOP.left_distance = steelsquid_piio.hcsr04_distance(SETTINGS.left_trig_gpio, SETTINGS.left_echo_gpio) LOOP.front_left_distance = steelsquid_piio.hcsr04_distance(SETTINGS.front_left_trig_gpio, SETTINGS.front_left_echo_gpio) return 0.3 # Execute this method again in half a second class WEB(object): ''' Methods in this class will be executed by the webserver if module is enabled and the webserver is enabled If is a GET it will return files and if it is a POST it executed commands. It is meant to be used as follows. 1. Make a call from the browser (GET) and a html page is returned back. 2. This html page then make AJAX (POST) call to the server to retrieve or update data. 3. The data sent to and from the server can just be a simple list of strings. For more examples how it work: - steelsquid_http_server.py - steelsquid_kiss_http_server.py - web/index.html ''' # Current servo position servo_position = 210 # Is the lamp on or off lamp_status = False # Last time the client send a drive command last_drive_command = datetime.datetime.now() @staticmethod def rover_settings(session_id, parameters): ''' Get info on the rover ''' return [WEB.servo_position, SETTINGS.servo_position_max, SETTINGS.servo_position_min, SETTINGS.motor_forward_max, SETTINGS.motor_backward_max, SETTINGS.motor_forward_start, SETTINGS.motor_backward_start, WEB.lamp_status, SETTINGS.rover_type] @staticmethod def rover_status(session_id, parameters): ''' Get status ''' return [steelsquid_piio.volt_last(), LOOP.front_distance, LOOP.front_right_distance, LOOP.right_distance, LOOP.back_right_distance, LOOP.back_distance, LOOP.back_left_distance, LOOP.left_distance, LOOP.front_left_distance] @staticmethod def rover_camera(session_id, parameters): ''' Tilt camera up and down ''' position = int(parameters[0]) if position>SETTINGS.servo_position_max: position=SETTINGS.servo_position_max elif position<SETTINGS.servo_position_min: position=SETTINGS.servo_position_min WEB.servo_position = position steelsquid_piio.servo(1, position) @staticmethod def rover_drive(session_id, parameters): ''' Tilt camera up and down ''' left = int(parameters[0]) right = int(parameters[1]) if left>SETTINGS.motor_forward_max: left=SETTINGS.motor_forward_max elif left<SETTINGS.motor_backward_max: left=SETTINGS.motor_backward_max if right>SETTINGS.motor_forward_max: right=SETTINGS.motor_forward_max elif right<SETTINGS.motor_backward_max: right=SETTINGS.motor_backward_max WEB.last_drive_command = datetime.datetime.now() # The large rover uses Piborg diablo motor controller if SETTINGS.rover_type=="large": steelsquid_pi.diablo_motor_1(left); steelsquid_pi.diablo_motor_2(right); else: steelsquid_piio.motor(left, right) @staticmethod def rover_lamp(session_id, parameters): ''' Turn the lamp on and off ''' status = steelsquid_utils.to_boolean(parameters[0]) steelsquid_piio.power(SETTINGS.lamp_power_pin, status) steelsquid_piio.power(SETTINGS.lamp_power_pin+1, status) rover.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Kiss OS Rover</title> <meta name="Steelsquid Kiss OS" content="Controll a rover with Steelsquid PIIO board" /> <meta name="keywords" content="steelsquid, steelsquid-kiss-os, raspberry, pi, debian, linux, distribution, administrator, configure, rover" /> <meta name="author" content="Andreas Nilsson (Steelsquid)" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/> <meta name="mobile-web-app-capable" content="yes"/> <meta name="apple-mobile-web-app-capable" content="yes"/> <link rel="icon" href="favicon.ico" type="image/x-icon"/> <link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/> <link rel="stylesheet" type="text/css" href="steelsquid.css"/> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="steelsquid.js"></script> <style type="text/css"> /* Necessary for correct mouse co-ords in Firefox */ #app { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } /* Necessary for correct mouse co-ords in Firefox */ #pad { position:relative; } </style> <script type="text/javascript"> //<![CDATA[ // Is this a touch device var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0)); // Variables for referencing the canvas and 2dcanvas context var canvas; var ctx; // Video location videoLeft=0; videoTop=0; videoWidth=0; videoHeight=0; /** * Draws a dot at a specific position on the canvas */ function drawDot(x, y ,size) { // Let's use black by setting RGB values to 0, and 255 alpha (completely opaque) r=0; g=0; b=0; a=255; // Select a fill style ctx.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")"; // Draw a filled circle ctx.beginPath(); ctx.arc(x, y, size, 0, Math.PI*2, true); ctx.closePath(); ctx.fill(); } /** * Draws a line */ function drawLine(x_start, y_start, x_stop, y_stop) { ctx.beginPath(); ctx.moveTo(x_start, y_start); ctx.lineTo(x_stop, y_stop); ctx.lineWidth = 4; ctx.strokeStyle = '#000000'; ctx.stroke(); } /** * Draws drawDistance */ function drawDistance() { if(videoWidth>0){ //Top left fromLeft = videoLeft; fromTop = videoTop-1; startAngle = 0; endAngle = Math.PI/2; drawPay(fromLeft, fromTop, startAngle, endAngle, front_left_distance); //Top fromLeft = videoLeft + videoWidth/2; fromTop = videoTop-1; startAngle = 0; endAngle = Math.PI; drawPay(fromLeft, fromTop, startAngle, endAngle, front_distance) //Top right fromLeft = videoLeft+videoWidth; fromTop = videoTop-1; startAngle = Math.PI/2; endAngle = Math.PI; drawPay(fromLeft, fromTop, startAngle, endAngle, front_right_distance); //Right fromLeft = videoLeft+videoWidth; fromTop = videoTop-36 + videoHeight/2; startAngle = Math.PI/2; endAngle = Math.PI*1.5; drawPay(fromLeft, fromTop, startAngle, endAngle, right_distance); //Back Right fromLeft = videoLeft+videoWidth; fromTop = videoTop + videoHeight-36; startAngle = Math.PI; endAngle = Math.PI*1.5; drawPay(fromLeft, fromTop, startAngle, endAngle, back_right_distance); //Back fromLeft = videoLeft+videoWidth/2; fromTop = videoTop + videoHeight-36; startAngle = Math.PI; endAngle = 0; drawPay(fromLeft, fromTop, startAngle, endAngle, back_distance); //Back left fromLeft = videoLeft; fromTop = videoTop + videoHeight-36; startAngle = Math.PI*1.5; endAngle = 0; drawPay(fromLeft, fromTop, startAngle, endAngle, back_left_distance); //left fromLeft = videoLeft; fromTop = videoTop-36 + videoHeight/2; startAngle = Math.PI*1.5; endAngle = Math.PI*0.5; drawPay(fromLeft, fromTop, startAngle, endAngle, left_distance); } } function drawPay(fromLeft, fromTop, startAngle, endAngle, distance) { radius = videoHeight/15; dest_rad = remap(distance, 4, 80, radius-3, 0) ctx.beginPath(); ctx.strokeStyle = '#000000'; ctx.lineWidth = 2; ctx.arc(fromLeft, fromTop, radius, startAngle, endAngle, false); ctx.stroke(); ctx.beginPath(); ctx.strokeStyle = '#ffffff'; ctx.lineWidth = 2; ctx.arc(fromLeft, fromTop, radius-2, startAngle, endAngle, false); ctx.stroke(); ctx.beginPath(); ctx.moveTo(fromLeft, fromTop); ctx.fillStyle = '#ff0000'; ctx.arc(fromLeft, fromTop, dest_rad, startAngle, endAngle, false); ctx.closePath(); ctx.fill(); } /** * Draws text on canvas */ function drawText(text, x, y) { ctx.fillStyle = '#000000'; ctx.font = "13px Arial"; ctx.fillText(text, x, y); } /** * Draws text vertical on canvas */ function drawTextVerical(text, toLeft) { var x = canvas.offsetWidth-8; var y = (canvas.offsetHeight / 2)-20; ctx.save(); if (toLeft){ ctx.translate(16, y); } else{ ctx.translate(x, y); } ctx.fillStyle = '#000000'; ctx.rotate(-Math.PI/2); ctx.textAlign = "center"; ctx.font = "13px Arial"; ctx.fillText(text, 0, 0); ctx.restore(); } /** * Draws text vertical in canvas */ function drawDoubleText(verticalText1, verticalText2) { var x = canvas.offsetWidth-8; var y = (canvas.offsetHeight / 2)-20; ctx.save(); ctx.fillStyle = '#000000'; ctx.translate(x, y); ctx.rotate(-Math.PI/2); ctx.textAlign = "center"; ctx.font = "13px Arial"; ctx.fillText(verticalText1, 0, 0); ctx.restore(); ctx.save(); ctx.translate(x-20, y); ctx.rotate(-Math.PI/2); ctx.textAlign = "center"; ctx.font = "13px Arial"; ctx.fillText(verticalText2, 0, 0); ctx.restore(); } /** * Expand canvas to max size */ function setSizeOfCanvas(){ heightObj = document.getElementById('fill'); canvas.style.width = "100%"; canvas.style.height = (heightObj.offsetHeight)+"px"; canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; //Set size of video $("#stream_img").height(canvas.offsetHeight); max_value = parseInt(canvas.height/2); min_value = max_value*-1; } // Variables to keep track of the mouse position and left-button status var drive_is_mouseDown=0; var drive_mouseX_now; var drive_mouseY_now; var drive_mouseX_press; var drive_mouseY_press; var camera_is_mouseDown=0; var camera_mouseX_now; var camera_mouseY_now; var camera_mouseX_press; var camera_mouseY_press; var last_tap = new Date().getTime(); /** * Remap one serie to another */ function remap( x, oMin, oMax, nMin, nMax ){ //range check if (oMin == oMax){ return None; } if (nMin == nMax){ return None; } //check reversed input range var reverseInput = false; oldMin = Math.min( oMin, oMax ); oldMax = Math.max( oMin, oMax ); if (oldMin != oMin){ reverseInput = true; } //check reversed output range var reverseOutput = false; newMin = Math.min( nMin, nMax ); newMax = Math.max( nMin, nMax ); if (newMin != nMin){ reverseOutput = true; } var portion = (x-oldMin)*(newMax-newMin)/(oldMax-oldMin); if (reverseInput){ portion = (oldMax-x)*(newMax-newMin)/(oldMax-oldMin); } var result = portion + newMin; if (reverseOutput){ result = newMax - portion; if(result<nMax){ result = nMax; } else if(result>nMin){ result = nMin; } } else{ if(result>nMax){ result = nMax; } else if(result<nMin){ result = nMin; } } return result; } /** * Keep track of the mouse button being pressed and draw a dot at current location */ function mouseDown(e) { if (drive_mouseX_now>=($(document).width()/2)){ drive_is_mouseDown=1; drive_mouseX_press = drive_mouseX_now; drive_mouseY_press = drive_mouseY_now; //Stop the movement sendDrive(0, 0); } else{ camera_is_mouseDown=1; camera_mouseX_press = camera_mouseX_now; camera_mouseY_press = camera_mouseY_now; servo_position_press = servo_position; } // Draw canvas drawCanvas(); // Check if it is a double tap tapTheScreen(); } /** * Keep track of the mouse button being released */ function mouseUp(e) { drive_is_mouseDown=0; camera_is_mouseDown=0; // Draw canvas drawCanvas(); //Stop the movement sendDrive(0, 0); } /** * Keep track of the mouse position. */ function mouseMove(e) { if (!e) var e = event; e.preventDefault(); //Get current location if (e.offsetX) { drive_mouseX_now = e.offsetX; drive_mouseY_now = e.offsetY; camera_mouseX_now = e.offsetX; camera_mouseY_now = e.offsetY; } else if (e.layerX) { drive_mouseX_now = e.layerX; drive_mouseY_now = e.layerY; camera_mouseX_now = e.layerX; camera_mouseY_now = e.layerY; } if(drive_is_mouseDown){ // Draw canvas drawCanvas(); //Send a drive command to the rover sendDrive(drive_mouseX_now- drive_mouseX_press, drive_mouseY_press - drive_mouseY_now) } if(camera_is_mouseDown){ // Draw canvas drawCanvas(); //Send a camera move command to the rover if(rover_type=="large"){ sendCamera(camera_mouseY_press-camera_mouseY_now); } else{ sendCamera(camera_mouseY_now-camera_mouseY_press); } } } /** * When user touch screen */ function touchStart(e) { if (!e) var e = event; e.preventDefault(); var arrayLength = e.changedTouches.length; //Check all touches, touch on the left side is camera movement //And on the right side to drive the rover. for (var i = 0; i < arrayLength; i++) { t = e.changedTouches[i]; if (t.pageX>=($(document).width()/2)){ drive_is_mouseDown=1; drive_mouseX_press = t.pageX; drive_mouseY_press = t.pageY - canvas.offsetTop; // Draw canvas drawCanvas(); //Stop the movement sendDrive(0, 0); } else{ camera_is_mouseDown=1; camera_mouseX_press = t.pageX; camera_mouseY_press = t.pageY - canvas.offsetTop; // Draw canvas drawCanvas(); servo_position_press = servo_position; } } // Check if it is a double tap tapTheScreen(); } /** * When user stop to touch screen */ function touchEnd(e) { if (!e) var e = event; e.preventDefault(); var arrayLength = e.changedTouches.length; for (var i = 0; i < arrayLength; i++) { t = e.changedTouches[i]; if (t.pageX>=($(document).width()/2)){ drive_is_mouseDown=0; // Draw canvas drawCanvas(); //Stop the movement sendDrive(0, 0); } else{ camera_is_mouseDown=0; // Draw canvas drawCanvas(); } } } /** * When user move the finger on screen */ function touchMove(e) { if (!e) var e = event; e.preventDefault(); var arrayLength = e.changedTouches.length; for (var i = 0; i < arrayLength; i++) { t = e.changedTouches[i]; if (t.pageX>=($(document).width()/2)){ drive_mouseX_now = t.pageX; drive_mouseY_now = t.pageY - canvas.offsetTop; } else{ camera_mouseX_now = t.pageX; camera_mouseY_now = t.pageY - canvas.offsetTop; } } //Draw the drive if (drive_is_mouseDown==1) { // Draw canvas drawCanvas(); //Send a drive command to the rover sendDrive(drive_mouseX_now-drive_mouseX_press, drive_mouseY_press - drive_mouseY_now) } if (camera_is_mouseDown==1) { // Draw canvas drawCanvas(); //Send a camera move command to the rover if(rover_type=="large"){ sendCamera(camera_mouseY_press-camera_mouseY_now); } else{ sendCamera(camera_mouseY_now-camera_mouseY_press); } } } var rover_is_enabled = false var stream_port="8080"; var servo_position = 210; var servo_position_press = servo_position; var servo_position_last=servo_position; var servo_position_max = 230; var servo_position_min = 80; var motor_forward_max = 1023; var motor_backward_max = -1023; var motor_forward_start = 200; var motor_backward_start = -200; var motor_left = 0; var motor_right = 0; var max_value = 0; var min_value = 0; var send_drive = false; var lamp_status = false; var rover_type = "medium"; var every_second_time = true; var voltage = 0; var front_distance = 999; var front_right_distance=999 var right_distance=999 var back_right_distance=999 var back_distance=999 var back_left_distance=999 var left_distance=999 var front_left_distance=999 /** * Initialise all the drawing on the canvas here */ function drawCanvas() { //Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); //Draw voltage drawText("Voltage: "+voltage, canvas.offsetWidth-130, 15) //Draw help texts drawTextVerical("Drag up and down to tilt camera", true); drawDoubleText("Drag in all directions to drive the rover", "Double tap to toggle the light"); if(drive_is_mouseDown){ //Draw start dott drawDot(drive_mouseX_press, drive_mouseY_press, 12); //Draw new dott drawDot(drive_mouseX_now, drive_mouseY_now, 12); //Draw line from new to start dott drawLine(drive_mouseX_now, drive_mouseY_now, drive_mouseX_press, drive_mouseY_press); } if(camera_is_mouseDown){ //Draw start dott drawDot(camera_mouseX_press, camera_mouseY_press, 12); //Draw new dott drawDot(camera_mouseX_press, camera_mouseY_now, 12); //Draw line from new to start dott drawLine(camera_mouseX_press, camera_mouseY_now, camera_mouseX_press, camera_mouseY_press); } //Draw the distance stuff drawDistance(); } /** * When the page is loaded */ function onPageLoad(){ //Prevent scrolling document.body.addEventListener('touchmove', function(event) { event.preventDefault(); }, false); // Get the specific canvas element from the HTML document canvas = document.getElementById('pad'); // fit the canvas to container setSizeOfCanvas(); // If the browser supports the canvas tag, get the 2d drawing context for this canvas ctx = canvas.getContext('2d'); // Draw the canvas drawCanvas(); // Handle touch and desktop different if(isTouch){ canvas.addEventListener('touchstart', touchStart, false); canvas.addEventListener('touchmove', touchMove, false); canvas.addEventListener('touchend', touchEnd, false); } else{ canvas.addEventListener('mousedown', mouseDown, false); canvas.addEventListener('mousemove', mouseMove, false); window.addEventListener('mouseup', mouseUp, false); } // Set fit the canvas to container window.addEventListener('resize', function(event){ setSizeOfCanvas(); try{ // Draw the canvas drawCanvas(); } catch(e){ } }); // This thread sending the command to server setTimeout(function(){ if (servo_position_last!=servo_position){ servo_position_last = servo_position; submitAsync('rover_camera', servo_position); } if (motor_left!=0 || motor_right!=0){ send_drive=true; } if (send_drive){ m_l=motor_left; m_r=motor_right; if (m_l > 0){ m_l = m_l + motor_forward_start; } else if(m_l < 0){ m_l = m_l + motor_backward_start; } if (m_r > 0){ m_r = m_r + motor_forward_start; } else if(m_r < 0){ m_r = m_r + motor_backward_start; } submitAsync('rover_drive', m_l, m_r); } if (motor_left==0 && motor_right==0){ send_drive=false; } if(every_second_time){ submitNoPW('rover_status'); } every_second_time=!every_second_time; setTimeout(arguments.callee, 300); }, 300); } /** * This will execute every 1 second */ function onEvery01s(){ //Send utill is enabled if(!rover_is_enabled){ //Send a command to the servor to get settings submitNoPW('rover_settings'); } } /** * This will execute every 5 second */ function onEvery05s(){ if (rover_is_enabled && !is_top_menu_showing()){ $("#the_video").show(); $("#stream_img").attr("src", "http://"+window.location.hostname+":"+stream_port+"/?action=stream"); img_o = $("#stream_img"); p = img_o.position(); videoLeft=40; videoTop=1; videoWidth=img_o.width(); videoHeight=img_o.height(); } } /** * When the page is in front */ function onVisible(){ if (rover_is_enabled && !is_top_menu_showing()){ drawCanvas(); $("#the_video").show(); $("#stream_img").attr("src", "http://"+window.location.hostname+":"+stream_port+"/?action=stream"); img_o = $("#the_video"); p = img_o.position(); videoLeft=p.left; videoTop=p.left; videoWidth=img_o.width(); videoHeight=img_o.height(); } } /** * When the page is in background */ function onHidden(){ if (rover_is_enabled){ $("#the_video").hide(); $("#stream_img").attr("src", ""); } } /** * This will execute when the black top menu is expanded */ function on_top_menu_show(){ onHidden(); } /** * This will execute when the black top menu is hidden */ function on_top_menu_hide(){ onVisible(); } /** * Get settings from the server */ function on_rover_settings(paramaters){ servo_position = parseInt(paramaters[0]); servo_position_press = servo_position; servo_position_last = servo_position; servo_position_max = parseInt(paramaters[1]); servo_position_min = parseInt(paramaters[2]); motor_forward_max = parseInt(paramaters[3]); motor_backward_max = parseInt(paramaters[4]); motor_forward_start = parseInt(paramaters[5]); motor_backward_start = parseInt(paramaters[6]); lamp_status = paramaters[7]=="True"; rover_type = paramaters[8]; //Rotate camera if a mini rover if(rover_type=="mini"){ $('#stream_img').removeClass('rotate-180').addClass('rotate-180'); } rover_is_enabled=true; $("#not_enabled").hide(); onEvery05s(); } /** * Get status */ function on_rover_status(paramaters){ voltage = paramaters[0]; front_distance = parseInt(paramaters[1]); front_right_distance=parseInt(paramaters[2]); right_distance=parseInt(paramaters[3]); back_right_distance=parseInt(paramaters[4]); back_distance=parseInt(paramaters[5]); back_left_distance=parseInt(paramaters[6]); left_distance=parseInt(paramaters[7]); front_left_distance=parseInt(paramaters[8]); drawCanvas(); } /** * Error when get settings from the server */ function on_rover_settings_err(error){ rover_is_enabled=false; $("#not_enabled").show(); } /** * On enable the rover functionality OK */ function on_module_status(paramaters){ showAlertMedium("Enabling...please wait!!!", false); } /** * Send drive command to rover * x and y how many pixelsn the finger is drawn (top to bottom +, bottom to top -) */ function sendDrive(x, y) { drive = parseInt(remap(y, min_value, max_value, motor_backward_max, motor_forward_max)); motor_left = drive; motor_right = drive; steer = parseInt((remap(x, min_value, max_value, motor_backward_max, motor_forward_max))); if (steer>0){ //Right motor_right = motor_right - steer; motor_left = motor_left + steer; } else if (steer<0){ //Left motor_right = motor_right - steer; motor_left = motor_left + steer; } if (motor_right>motor_forward_max){ motor_right = motor_forward_max; } else if (motor_right<motor_backward_max){ motor_right = motor_backward_max; } if (motor_left>motor_forward_max){ motor_left = motor_forward_max; } else if (motor_left<motor_backward_max){ motor_left = motor_backward_max; } } /** * Send camera command to rover * y how many pixelsn the finger is drawn (top to bottom +, bottom to top -) */ function sendCamera(new_camera) { if (new_camera<0){ add_servo_position = (remap(new_camera*-1, 0, max_value, 0, servo_position_max-servo_position_min ))*-1; } else{ add_servo_position = remap(new_camera, 0, max_value, 0, servo_position_max-servo_position_min ); } servo_position = parseInt(servo_position_press + add_servo_position); if (servo_position<servo_position_min){ servo_position=servo_position_min; } else if (servo_position>servo_position_max){ servo_position=servo_position_max; } } /** * Check if it is a double tap, then toggle lamp */ function tapTheScreen(){ new_tap = new Date().getTime(); if(new_tap-last_tap<300){ lamp_status = !lamp_status; submitAsync('rover_lamp', lamp_status); } last_tap = new_tap; } //]]> </script> </head> <body id="the_body"> <div id="included_top_bar">Kiss OS Utils</div> <div class="fill" id="fill"> <div id="app"> <canvas id="pad" style="z-index: 10"> </canvas> </div> </div> <div id="the_video" style="position:absolute; left:40px;top:37px;display: none;z-index: 5"> <img alt="The stream" id="stream_img" src="" style="height:100%"> </div> <div id="not_enabled" style="position:absolute; left:10px;top:60px;display: none;border:1px solid black;background-color: white;padding:8px"> The rover fuctionality is not enabled.<br/> <!-- Send command to server to enable the rover module --> <button onclick="javascript:submitSync('module_status', 'kiss_rover', 'True');">Enable</button> </div> </body> </html> |