diff --git a/README.md b/README.md index ee1dffc7..18a726df 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ See self-driving in action This project builds a self-driving RC car using Raspberry Pi, Arduino and open source software. Raspberry Pi collects inputs from a camera module and an ultrasonic sensor, and sends data to a computer wirelessly. The computer processes input images and sensor data for object detection (stop sign and traffic light) and collision avoidance respectively. A neural network model runs on computer and makes predictions for steering based on input images. Predictions are then sent to the Arduino for RC car control. +**Note: This is the code for running a car powered with an esp8266 and L293 driver** + ### Setting up environment with Anaconda 1. Install [`miniconda(Python3)`](https://conda.io/miniconda.html) on your computer 2. Create `auto-rccar` environment with all necessary libraries for this project @@ -31,8 +33,8 @@ This project builds a self-driving RC car using Raspberry Pi, Arduino and open s     `stream_client.py`: stream video frames in jpeg format to the host computer     `ultrasonic_client.py`: send distance data measured by sensor to the host computer -**arduino/** -     `rc_keyboard_control.ino`: control RC car controller +**esp/** +     `boot.py`: RC car controller **computer/**     **cascade_xml/** @@ -54,7 +56,12 @@ This project builds a self-driving RC car using Raspberry Pi, Arduino and open s ### How to drive -1. **Testing:** Flash `rc_keyboard_control.ino` to Arduino and run `rc_control_test.py` to drive the RC car with keyboard. Run `stream_server_test.py` on computer and then run `stream_client.py` on raspberry pi to test video streaming. Similarly, `ultrasonic_server_test.py` and `ultrasonic_client.py` can be used for sensor data streaming testing. + +***Note: Remember to do the TODO in the following file:*** + +   `config.py` **line 2** + +1. **Testing:** Flash `boot.py`, `config.py` & `webrepl_cfg.py` to ESP8266 and run `rc_control_test.py` to drive the RC car with keyboard. Run `stream_server_test.py` on computer and then run `stream_client.py` on raspberry pi to test video streaming. Similarly, `ultrasonic_server_test.py` and `ultrasonic_client.py` can be used for sensor data streaming testing. 2. **Pi Camera calibration (optional):** Take multiple chess board images using pi camera module at various angles and put them into **`chess_board`** folder, run `picam_calibration.py` and returned parameters from the camera matrix will be used in `rc_driver.py`. diff --git a/__pycache__/config.cpython-38.pyc b/__pycache__/config.cpython-38.pyc new file mode 100644 index 00000000..eaea83c0 Binary files /dev/null and b/__pycache__/config.cpython-38.pyc differ diff --git a/arduino/README.md b/arduino/README.md deleted file mode 100644 index 4999629f..00000000 --- a/arduino/README.md +++ /dev/null @@ -1 +0,0 @@ -Inspired by this aiticle: [Drive a Lamborghini With Your Keyboard](http://thelivingpearl.com/2013/01/04/drive-a-lamborghini-with-your-keyboard/) diff --git a/arduino/rc_keyboard_control.ino b/arduino/rc_keyboard_control.ino deleted file mode 100644 index 9f5b7eef..00000000 --- a/arduino/rc_keyboard_control.ino +++ /dev/null @@ -1,102 +0,0 @@ -// assign pin num -int right_pin = 6; -int left_pin = 7; -int forward_pin = 10; -int reverse_pin = 9; - -// duration for output -int time = 50; -// initial command -int command = 0; - -void setup() { - pinMode(right_pin, OUTPUT); - pinMode(left_pin, OUTPUT); - pinMode(forward_pin, OUTPUT); - pinMode(reverse_pin, OUTPUT); - Serial.begin(115200); -} - -void loop() { - //receive command - if (Serial.available() > 0){ - command = Serial.read(); - } - else{ - reset(); - } - send_command(command,time); -} - -void right(int time){ - digitalWrite(right_pin, LOW); - delay(time); -} - -void left(int time){ - digitalWrite(left_pin, LOW); - delay(time); -} - -void forward(int time){ - digitalWrite(forward_pin, LOW); - delay(time); -} - -void reverse(int time){ - digitalWrite(reverse_pin, LOW); - delay(time); -} - -void forward_right(int time){ - digitalWrite(forward_pin, LOW); - digitalWrite(right_pin, LOW); - delay(time); -} - -void reverse_right(int time){ - digitalWrite(reverse_pin, LOW); - digitalWrite(right_pin, LOW); - delay(time); -} - -void forward_left(int time){ - digitalWrite(forward_pin, LOW); - digitalWrite(left_pin, LOW); - delay(time); -} - -void reverse_left(int time){ - digitalWrite(reverse_pin, LOW); - digitalWrite(left_pin, LOW); - delay(time); -} - -void reset(){ - digitalWrite(right_pin, HIGH); - digitalWrite(left_pin, HIGH); - digitalWrite(forward_pin, HIGH); - digitalWrite(reverse_pin, HIGH); -} - -void send_command(int command, int time){ - switch (command){ - - //reset command - case 0: reset(); break; - - // single command - case 1: forward(time); break; - case 2: reverse(time); break; - case 3: right(time); break; - case 4: left(time); break; - - //combination command - case 6: forward_right(time); break; - case 7: forward_left(time); break; - case 8: reverse_right(time); break; - case 9: reverse_left(time); break; - - default: Serial.print("Inalid Command\n"); - } -} diff --git a/computer/__pycache__/model.cpython-38.pyc b/computer/__pycache__/model.cpython-38.pyc new file mode 100644 index 00000000..254dd3ac Binary files /dev/null and b/computer/__pycache__/model.cpython-38.pyc differ diff --git a/computer/__pycache__/rc_driver_helper.cpython-38.pyc b/computer/__pycache__/rc_driver_helper.cpython-38.pyc new file mode 100644 index 00000000..a591fef4 Binary files /dev/null and b/computer/__pycache__/rc_driver_helper.cpython-38.pyc differ diff --git a/computer/collect_training_data.py b/computer/collect_training_data.py index facce4bc..dcd7847c 100644 --- a/computer/collect_training_data.py +++ b/computer/collect_training_data.py @@ -1,5 +1,11 @@ __author__ = 'zhengwang' +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + import numpy as np import cv2 import serial @@ -7,12 +13,13 @@ from pygame.locals import * import socket import time -import os +import urllib.request as httpReq +import config class CollectTrainingData(object): - def __init__(self, host, port, serial_port, input_size): + def __init__(self, host, port, input_size): self.server_socket = socket.socket() self.server_socket.bind((host, port)) @@ -20,9 +27,10 @@ def __init__(self, host, port, serial_port, input_size): # accept a single connection self.connection = self.server_socket.accept()[0].makefile('rb') + self.httpURL = config.httpURL - # connect to a seral port - self.ser = serial.Serial(serial_port, 115200, timeout=1) + # connect to a serial port + # self.ser = serial.Serial(serial_port, 115200, timeout=1) self.send_inst = True self.input_size = input_size @@ -81,62 +89,72 @@ def collect(self): # complex orders if key_input[pygame.K_UP] and key_input[pygame.K_RIGHT]: - print("Forward Right") X = np.vstack((X, temp_array)) y = np.vstack((y, self.k[1])) saved_frame += 1 - self.ser.write(chr(6).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,0)&motrCtrl(2,1)?/") + print("Forward Right: " + res) + # self.ser.write(chr(6).encode()) elif key_input[pygame.K_UP] and key_input[pygame.K_LEFT]: - print("Forward Left") X = np.vstack((X, temp_array)) y = np.vstack((y, self.k[0])) saved_frame += 1 - self.ser.write(chr(7).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,0)?/") + print("Forward Left: " + res) + # self.ser.write(chr(7).encode()) elif key_input[pygame.K_DOWN] and key_input[pygame.K_RIGHT]: - print("Reverse Right") - self.ser.write(chr(8).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,-1)&motrCtrl(2,1)?/") + print("Reverse Right: " + res) + # self.ser.write(chr(8).encode()) elif key_input[pygame.K_DOWN] and key_input[pygame.K_LEFT]: - print("Reverse Left") - self.ser.write(chr(9).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,-1)?/") + print("Reverse Left: " + res) + # self.ser.write(chr(9).encode()) # simple orders elif key_input[pygame.K_UP]: - print("Forward") saved_frame += 1 X = np.vstack((X, temp_array)) y = np.vstack((y, self.k[2])) - self.ser.write(chr(1).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,1)?/") + print("Forward: " + res) + # self.ser.write(chr(1).encode()) elif key_input[pygame.K_DOWN]: - print("Reverse") - self.ser.write(chr(2).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,-1)&motrCtrl(2,-1)?/") + print("Reverse: " + res) + # self.ser.write(chr(2).encode()) elif key_input[pygame.K_RIGHT]: - print("Right") X = np.vstack((X, temp_array)) y = np.vstack((y, self.k[1])) saved_frame += 1 - self.ser.write(chr(3).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,0)&motrCtrl(2,1)?/") + print("Right: " + res) + # self.ser.write(chr(3).encode()) elif key_input[pygame.K_LEFT]: - print("Left") X = np.vstack((X, temp_array)) y = np.vstack((y, self.k[0])) saved_frame += 1 - self.ser.write(chr(4).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,0)?/") + print("Left: " + res) + # self.ser.write(chr(4).encode()) elif key_input[pygame.K_x] or key_input[pygame.K_q]: - print("exit") self.send_inst = False - self.ser.write(chr(0).encode()) - self.ser.close() + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,0)&motrCtrl(2,0)?/") + print("Exit: " + res) + # self.ser.close() break elif event.type == pygame.KEYUP: - self.ser.write(chr(0).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,0)&motrCtrl(2,0)?/") + print("Exit: " + res) + # self.ser.write(chr(0).encode()) if cv2.waitKey(1) & 0xFF == ord('q'): break @@ -168,13 +186,10 @@ def collect(self): if __name__ == '__main__': # host, port - h, p = "192.168.1.100", 8000 - - # serial port - sp = "/dev/tty.usbmodem1421" + h, p = config.serverIP, 8000 # vector size, half of the image s = 120 * 320 - ctd = CollectTrainingData(h, p, sp, s) + ctd = CollectTrainingData(h, p, s) ctd.collect() diff --git a/computer/model_train_test/data_test.npz b/computer/model_train_test/data_test.npz new file mode 100644 index 00000000..18150dd1 Binary files /dev/null and b/computer/model_train_test/data_test.npz differ diff --git a/computer/model_train_test/train_predict_test.ipynb b/computer/model_train_test/train_predict_test.ipynb new file mode 100644 index 00000000..9e75c408 --- /dev/null +++ b/computer/model_train_test/train_predict_test.ipynb @@ -0,0 +1,263 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import cv2\n", + "import numpy as np\n", + "import glob\n", + "import sys\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OpenCV: 3.4.2\n", + "Numpy : 1.15.0\n", + "Python: 3.7.0 (default, Jun 28 2018, 13:15:42) \n", + "[GCC 7.2.0]\n" + ] + } + ], + "source": [ + "print (\"OpenCV:\", cv2.__version__)\n", + "print (\"Numpy : \", np.__version__)\n", + "print (\"Python:\", sys.version)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Image array shape: (10, 76800)\n", + "Label array shape: (10, 4)\n" + ] + } + ], + "source": [ + "# load training data\n", + "dim = 240*320\n", + "X = np.empty((0, dim))\n", + "y = np.empty((0, 4))\n", + "training_data = glob.glob('data_test.npz')\n", + "\n", + "for single_npz in training_data:\n", + " with np.load(single_npz) as data:\n", + " train = data['train']\n", + " train_labels = data['train_labels']\n", + " X = np.vstack((X, train))\n", + " y = np.vstack((y, train_labels))\n", + "\n", + "print ('Image array shape: ', X.shape)\n", + "print ('Label array shape: ', y.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(X[0].reshape(240, 320), cmap='gray')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# create a neural network\n", + "model = cv2.ml.ANN_MLP_create()\n", + "layer_sizes = np.int32([dim, 32, 4])\n", + "model.setLayerSizes(layer_sizes)\n", + "model.setTrainMethod(cv2.ml.ANN_MLP_BACKPROP)\n", + "model.setActivationFunction(cv2.ml.ANN_MLP_SIGMOID_SYM, 2, 1)\n", + "model.setTermCriteria((cv2.TERM_CRITERIA_COUNT, 20, 0.01))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# training\n", + "model.train(np.float32(X), cv2.ml.ROW_SAMPLE, np.float32(y))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10\n", + "[1 1 2 2 2 2 2 2 2 2]\n", + "Train accuracy: 80.00%\n" + ] + } + ], + "source": [ + "# evaluate on training data\n", + "ret, resp = model.predict(X)\n", + "prediction = resp.argmax(-1)\n", + "true_labels = y.argmax(-1)\n", + "\n", + "train_rate = np.mean(prediction == true_labels)\n", + "print (len(prediction))\n", + "print (prediction)\n", + "print ('Train accuracy: ', \"{0:.2f}%\".format(train_rate * 100))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# save model\n", + "model.save('model_test.xml')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# load model\n", + "model = cv2.ml.ANN_MLP_load('model_test.xml')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10\n" + ] + }, + { + "data": { + "text/plain": [ + "array([1, 1, 2, 2, 2, 2, 2, 2, 2, 2])" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# predict\n", + "ret, resp = model.predict(X)\n", + "print (len(resp))\n", + "resp.argmax(-1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.0" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/computer/rc_driver.py b/computer/rc_driver.py index 5f4c37a9..e5061b34 100644 --- a/computer/rc_driver.py +++ b/computer/rc_driver.py @@ -1,11 +1,16 @@ __author__ = 'zhengwang' +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + import cv2 -import sys import threading import socketserver import numpy as np - +import config from model import NeuralNetwork from rc_driver_helper import * @@ -39,7 +44,7 @@ class VideoStreamHandler(socketserver.StreamRequestHandler): nn.load_model("saved_model/nn_model.xml") obj_detection = ObjectDetection() - rc_car = RCControl("/dev/tty.usbmodem1421") + rc_car = RCControl() # cascade classifiers stop_cascade = cv2.CascadeClassifier("cascade_xml/stop_sign.xml") @@ -183,7 +188,7 @@ def start(self): if __name__ == '__main__': - h, p1, p2 = "192.168.1.100", 8000, 8002 + h, p1, p2 = config.serverIP, 8000, 8002 ts = Server(h, p1, p2) ts.start() diff --git a/computer/rc_driver_helper.py b/computer/rc_driver_helper.py index 7045f948..e3454c36 100644 --- a/computer/rc_driver_helper.py +++ b/computer/rc_driver_helper.py @@ -1,30 +1,45 @@ __author__ = 'zhengwang' +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + import serial import cv2 import math +import urllib.request as httpReq +import config class RCControl(object): - def __init__(self, serial_port): - self.serial_port = serial.Serial(serial_port, 115200, timeout=1) + def __init__(self): + # self.serial_port = serial.Serial(serial_port, 115200, timeout=1) + # http://192.168.4.1/?motrCtrl(1,mode=0)&motrCtrl(2,mode=0)?/ + self.httpURL = config.httpURL def steer(self, prediction): if prediction == 2: - self.serial_port.write(chr(1).encode()) - print("Forward") + # self.serial_port.write(chr(1).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,1)?/") + print("Forward: " + res) elif prediction == 0: - self.serial_port.write(chr(7).encode()) - print("Left") + # self.serial_port.write(chr(7).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,-1)&motrCtrl(2,1)?/") + print("Left: " + res) elif prediction == 1: - self.serial_port.write(chr(6).encode()) - print("Right") + # self.serial_port.write(chr(6).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,-1)?/") + print("Right: " + res) else: self.stop() def stop(self): - self.serial_port.write(chr(0).encode()) + # self.serial_port.write(chr(0).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,0)&motrCtrl(2,0)?/") + print("Stop: "+ res) class DistanceToCamera(object): diff --git a/computer/rc_driver_nn_only.py b/computer/rc_driver_nn_only.py index 4cb1ff16..8053d565 100644 --- a/computer/rc_driver_nn_only.py +++ b/computer/rc_driver_nn_only.py @@ -1,15 +1,22 @@ +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + import cv2 import numpy as np import socket import serial from model import NeuralNetwork from rc_driver_helper import RCControl +import config class RCDriverNNOnly(object): - def __init__(self, host, port, serial_port, model_path): + def __init__(self, host, port, model_path): self.server_socket = socket.socket() self.server_socket.bind((host, port)) @@ -22,7 +29,7 @@ def __init__(self, host, port, serial_port, model_path): self.nn = NeuralNetwork() self.nn.load_model(model_path) - self.rc_car = RCControl(serial_port) + self.rc_car = RCControl() def drive(self): stream_bytes = b' ' @@ -65,13 +72,10 @@ def drive(self): if __name__ == '__main__': # host, port - h, p = "192.168.1.100", 8000 - - # serial port - sp = "/dev/tty.usbmodem1421" + h, p = config.serverIP, 8000 # model path path = "saved_model/nn_model.xml" - rc = RCDriverNNOnly(h, p, sp, path) + rc = RCDriverNNOnly(h, p, path) rc.drive() diff --git a/config.py b/config.py new file mode 100644 index 00000000..80b423a8 --- /dev/null +++ b/config.py @@ -0,0 +1,5 @@ +# TODO: change this to match the PC's local IP. Leave the port unchanged +serverIP = "192.168.43.94" + +#do not change this if you are connected to autoRc network +httpURL = "http://192.168.43.250/?" \ No newline at end of file diff --git a/esp/README.md b/esp/README.md new file mode 100644 index 00000000..3c5044ac --- /dev/null +++ b/esp/README.md @@ -0,0 +1,14 @@ +You need an esp8266 running on micropython + +download - http://micropython.org/download#esp8266 +installation - http://docs.micropython.org/en/latest/esp8266/tutorial/intro.html#intro + +then upload the + +boot.py +webrepl_cfg.py +config.py + +files to the board and reboot + +By default, it should create a hotspot with the name "RC-MAC", where MAC is the first six characters of the device's mac address. The password to connect is "consolePass" (without quotes) \ No newline at end of file diff --git a/esp/boot.py b/esp/boot.py new file mode 100644 index 00000000..1fc4c050 --- /dev/null +++ b/esp/boot.py @@ -0,0 +1,262 @@ +# This file is executed on every boot (including wake-boot from deepsleep) +#import esp +#esp.osdebug(None) +import os, uos, utime, gc, webrepl +from machine import Pin as pin +#uos.dupterm(None, 1) # disable REPL on UART(0) + +webrepl.start() +gc.enable() + +# global varibles +DEBUG = True # enable or disable debugging +IP = '' # the server ip address + +# Logger to print to the terminal when the debug flag is set True +def log(msg): + if DEBUG: + print(msg) + +# wifi state flags +WIFI_ERROR = -1 +WIFI_DISABLED = 0 +WIFI_ENABLED = 1 +WIFI_CONNECTED = 2 +WIFI_AP = 3 + +#I2C flags +I2C_FOUND = 1 +I2C_NIL = 0 + +# wifi flag setter +def updateWifiState(state = WIFI_ENABLED): + global wifi_state + wifi_state = state + log('wifi state set to %d'%(state)) + +# i2c flag setter +def updateI2CState(state = I2C_NIL): + global i2c_state + i2c_state = state + log('i2c state set to %d'%(state)) + +# ip flag setter +def updateIP(ip): + global IP + IP = ip + log('ip set to %s'%str(ip)) + +# wifi flag getter +def getWifiState(): + global wifi_state + return wifi_state + +# i2c flag getter +def getI2CState(): + global i2c_state + return i2c_state + +# ip flag getter +def getIP(): + global IP + return IP + +# runtime flags +updateWifiState(WIFI_DISABLED) +updateI2CState(I2C_NIL) + +# The first_boot routine first sets up the access point +# Then helps to configure the default wifi network +# ESSID: console AP +# Password: consolePass +def first_boot(): + import network + import ubinascii + # first configure as a access point + + # get the MAC address + ap = network.WLAN(network.AP_IF) + ap_mac = ubinascii.hexlify(ap.config('mac'),':').decode() + + # strip the last 6 characters to add them to the essid + ap_mac = ap_mac[9::] + ap_mac = ap_mac.split(':') + ap_essid = ('RC-%s%s%s' %(ap_mac[0], ap_mac[1], ap_mac[2])) + + # configure the access point and enable it + ap.active(True) + ap.config(essid = ap_essid, password = 'consolePass', channel = 1, authmode = 4) + if ap.active(): + log('%s active:' % str(ap_essid)) + updateWifiState(WIFI_AP) + utime.sleep_ms(500) + else: + log('could not initiate AP!! Try resetting device') + updateWifiState(WIFI_ERROR) + +# The connect_wifi(ssid, authKey, disableAP) routine connects to an +# existing wifi network. It takes the following params: +# SSID : (String) the ssid of the wifi network to connect to +# authKey : (String) the password +# disableAP: (boolean) should disable built-in access point or not +def connect_wifi(ssid, authKey, disableAP): + import network + sta_if = network.WLAN(network.STA_IF) + updateWifiState(WIFI_ENABLED) + if not sta_if.isconnected(): + sta_if.active(True) + log('connecting to %s Please wait' %str(ssid)) + sta_if.connect(ssid, authKey) + while not sta_if.isconnected(): + pass + + utime.sleep_ms(3000) + if not sta_if.isconnected(): + log('Could not connect to %s! Enabling Access Point' %str(ssid)) + updateWifiState(WIFI_ERROR) + sta_if.active(False) + utime.sleep_ms(500) + first_boot() + else: + sta_if.ifconfig(('192.168.43.250','255.255.255.0','192.168.43.233','192.168.43.233')) + log('Connected to %s @ %s' %(str(ssid), str(sta_if.ifconfig()[0]))) + updateWifiState(WIFI_CONNECTED) + # disable the access point + (network.WLAN(network.AP_IF)).active(not disableAP) + +# The initial setup method +# Search for the config.cfg file. If it does not exist, excute the first_run routine +# If exists, connect to an existing network and configure MQTT +# override : (boolean) force into firstboot +def search_cfg(override): + inDir = os.listdir() + if (override) or ('config.py' not in inDir): + # config not exists - execute first_boot() + log('first boot') + first_boot() + else: + import config + connect_wifi(config.wifi_ssid, config.wifi_key, config.wifi_autoConnect) + pass + +search_cfg(False) + + +# mode - 0 => st +# mode - 1 => fw +# mode - 2 => bw + +#motPin - 1 => left motor +#motPin - 2 => right motor +def motrCtrl(motPin, mode): + #pin D2 + p1 = pin(4, pin.OUT) + + #pin D1 + p2 = pin(5, pin.OUT) + + #pin D3 + p3 = pin(0, pin.OUT) + + #pin D4 + p4 = pin(2, pin.OUT) + + #p1 = pin(12, pin.OUT) + #p2 = pin(14, pin.OUT) + + #p3 = pin(13, pin.OUT) + #p4 = pin(15, pin.OUT) + + if (motPin == 1): + if (mode == 0): + p1.value(0) + p2.value(0) + elif (mode > 0): + p1.value(1) + p2.value(0) + elif (mode < 0): + p1.value(0) + p2.value(1) + elif (motPin == 2): + if (mode == 0): + p3.value(0) + p4.value(0) + elif (mode > 0): + p3.value(1) + p4.value(0) + elif (mode < 0): + p3.value(0) + p4.value(1) + +#stop both motors by default +motrCtrl(1, 0) +motrCtrl(2, 0) + +# Handle the query from the GET request +# Takes the query string as arg +def handleGET(query): + gc.collect() + # import pwm, sensors, gpio, utime, dog + query = query.replace('&','\r\n') + query = query.replace('%20',' ') + query = query.replace('%27','\'') + # exec(query, {'pwm': pwm, 'gpio': gpio, 'sensors': sensors, 'utime': utime}) + exec(query) + +# create a webserver to read all the inputs from the remote +# supports only live mode +def createServer(): + if (wifi_state != WIFI_ERROR or wifi_state != WIFI_DISABLED or wifi_state != WIFI_ENABLED): + log('initiating server') + # create a socket obj + import socket, network + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # get the ip address if connected to a foreign network + serverIp = '192.168.43.250' + if (wifi_state == WIFI_CONNECTED): + conn = network.WLAN(network.STA_IF) + serverIp = conn.ifconfig()[0] + + sock.bind((serverIp, 80)) + sock.listen(1) + log('server running at %s port 80'%str(serverIp)) + + while True: + cxn, addr = sock.accept() + log('incoming from %s'%str(addr)) + req = cxn.recv(500) + req = str(req) + log('request: %s'%req) + sendBack = 'true' + + # process the request here and only if it is not a favicon req + try: + if (req.find('/?') != -1): + # it is a search query + query = req.split("/?") + log('firstPass: %s'%str(query)) + # filter out the favicon query + if (req.find('/favicon.ico') == -1): + query = query[1].split('?/')[0] + log('secondPass: %s'%str(query)) + # look for queries demanding a return + sendBack = handleGET(query) + cxn.send('HTTP/1.1 200 OK\n') + except Exception as e: + log('something went wrong: %s'%str(e)) + cxn.send('HTTP/1.1 405 Method Not Allowed\n') + + + cxn.send('Content-Type: text/html\n') + cxn.send('Connection: close\n\n') + cxn.sendall('Connection Successful!%s'%(sendBack)) + cxn.close() + +createServer() + + + + + + diff --git a/esp/config.py b/esp/config.py new file mode 100644 index 00000000..4ee0e4fc --- /dev/null +++ b/esp/config.py @@ -0,0 +1,8 @@ +#put your wifi SSID here +wifi_ssid = "autoRc" + +#put your wifi passphrase here +wifi_key = "autorccar" + +#do not bother +wifi_autoConnect = True \ No newline at end of file diff --git a/esp/test.py b/esp/test.py new file mode 100644 index 00000000..b28d5d5a --- /dev/null +++ b/esp/test.py @@ -0,0 +1,12 @@ +""" + Test file to check the working of config.py +""" + +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + +import config +print(config.serverIP) \ No newline at end of file diff --git a/esp/webrepl_cfg.py b/esp/webrepl_cfg.py new file mode 100644 index 00000000..0d30ecaf --- /dev/null +++ b/esp/webrepl_cfg.py @@ -0,0 +1,2 @@ +PASS = '1234' + diff --git a/raspberryPi/stream_client.py b/raspberryPi/stream_client.py index 7cee3233..c26a3488 100644 --- a/raspberryPi/stream_client.py +++ b/raspberryPi/stream_client.py @@ -1,18 +1,25 @@ +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + +import config import io import socket import struct import time -import picamera +from picamera import PiCamera # create socket and bind host client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -client_socket.connect(('192.168.1.100', 8000)) +client_socket.connect((config.serverIP, 8000)) connection = client_socket.makefile('wb') try: - with picamera.PiCamera() as camera: + with PiCamera() as camera: camera.resolution = (320, 240) # pi camera resolution camera.framerate = 15 # 15 frames/sec time.sleep(2) # give 2 secs for camera to initilize diff --git a/raspberryPi/stream_client_fast.py b/raspberryPi/stream_client_fast.py index 659a9122..b3199c5f 100644 --- a/raspberryPi/stream_client_fast.py +++ b/raspberryPi/stream_client_fast.py @@ -1,9 +1,16 @@ +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + +import config + import io import socket import struct import time import picamera -import sys class SplitFrames(object): def __init__(self, connection): @@ -25,7 +32,8 @@ def write(self, buf): self.stream.seek(0) self.stream.write(buf) -my_server = '192.168.1.100' + +my_server = config.serverIP res = (320, 240) client_socket = socket.socket() client_socket.connect((my_server, 8000)) diff --git a/raspberryPi/ultrasonic_client.py b/raspberryPi/ultrasonic_client.py index 6b762276..676f821b 100644 --- a/raspberryPi/ultrasonic_client.py +++ b/raspberryPi/ultrasonic_client.py @@ -1,3 +1,10 @@ +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + +import config from socket import * import time @@ -8,7 +15,7 @@ # create a socket and bind socket to the host client_socket = socket(AF_INET, SOCK_STREAM) -client_socket.connect(('192.168.1.100', 8002)) +client_socket.connect((config.serverIP, 8002)) def measure(): """ @@ -47,9 +54,9 @@ def measure(): try: while True: distance = measure() - print "Distance : %.1f cm" % distance + print ("Distance : %.1f cm" % distance) # send data to the host every 0.5 sec - client_socket.send(str(distance)) + client_socket.send(str(distance).encode()) time.sleep(0.5) finally: client_socket.close() diff --git a/test/rc_control_test.py b/test/rc_control_test.py index 2b2ebe0b..c839fb85 100644 --- a/test/rc_control_test.py +++ b/test/rc_control_test.py @@ -1,7 +1,16 @@ __author__ = 'zhengwang' +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + +import config + import serial import pygame +import urllib.request as httpReq from pygame.locals import * @@ -10,7 +19,8 @@ class RCTest(object): def __init__(self): pygame.init() pygame.display.set_mode((250, 250)) - self.ser = serial.Serial("/dev/tty.usbmodem1421", 115200, timeout=1) # mac + self.httpURL = config.httpURL + # self.ser = serial.Serial("/dev/tty.usbmodem1421", 115200, timeout=1) # mac # self.ser = serial.Serial("/dev/ttyACM0", 115200, timeout=1) # linux self.send_inst = True self.steer() @@ -24,48 +34,59 @@ def steer(self): # complex orders if key_input[pygame.K_UP] and key_input[pygame.K_RIGHT]: - print("Forward Right") - self.ser.write(chr(6).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,0)&motrCtrl(2,1)?/") + print("Forward Right: " + str(res)) + # self.ser.write(chr(6).encode()) elif key_input[pygame.K_UP] and key_input[pygame.K_LEFT]: - print("Forward Left") - self.ser.write(chr(7).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,0)?/") + print("Forward Left: " + str(res)) + # self.ser.write(chr(7).encode()) elif key_input[pygame.K_DOWN] and key_input[pygame.K_RIGHT]: - print("Reverse Right") - self.ser.write(chr(8).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,-1)&motrCtrl(2,1)?/") + print("Reverse Right: " + str(res)) + # self.ser.write(chr(8).encode()) elif key_input[pygame.K_DOWN] and key_input[pygame.K_LEFT]: - print("Reverse Left") - self.ser.write(chr(9).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,-1)?/") + print("Reverse Left: " + str(res)) + # self.ser.write(chr(9).encode()) # simple orders elif key_input[pygame.K_UP]: - print("Forward") - self.ser.write(chr(1).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,1)?/") + print("Forward: " + str(res)) + # self.ser.write(chr(1).encode()) elif key_input[pygame.K_DOWN]: - print("Reverse") - self.ser.write(chr(2).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,-1)&motrCtrl(2,-1)?/") + print("Reverse: " + str(res)) + # self.ser.write(chr(2).encode()) elif key_input[pygame.K_RIGHT]: - print("Right") - self.ser.write(chr(3).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,0)&motrCtrl(2,1)?/") + print("Right: " + str(res)) + # self.ser.write(chr(3).encode()) elif key_input[pygame.K_LEFT]: - print("Left") - self.ser.write(chr(4).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,1)&motrCtrl(2,0)?/") + print("Left: " + str(res)) + # self.ser.write(chr(4).encode()) # exit elif key_input[pygame.K_x] or key_input[pygame.K_q]: - print("Exit") + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,0)&motrCtrl(2,0)?/") + print("Exit: " + str(res)) self.send_inst = False - self.ser.write(chr(0).encode()) - self.ser.close() + # self.ser.write(chr(0).encode()) + # self.ser.close() break elif event.type == pygame.KEYUP: - self.ser.write(chr(0).encode()) + res = httpReq.urlopen(self.httpURL + "motrCtrl(1,0)&motrCtrl(2,0)?/") + print("Exit: " + str(res)) + # self.ser.write(chr(0).encode()) if __name__ == '__main__': diff --git a/test/stream_server_test.py b/test/stream_server_test.py index b9aba2bb..affa7246 100644 --- a/test/stream_server_test.py +++ b/test/stream_server_test.py @@ -1,8 +1,17 @@ __author__ = 'zhengwang' +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + +import config + import numpy as np import cv2 import socket +from time import sleep class VideoStreamingTest(object): @@ -18,7 +27,7 @@ def __init__(self, host, port): self.streaming() def streaming(self): - + print("server online") try: print("Host: ", self.host_name + ' ' + self.host_ip) print("Connection from: ", self.client_address) @@ -39,6 +48,9 @@ def streaming(self): if cv2.waitKey(1) & 0xFF == ord('q'): break + except Exception as exep: + print ("Error: %s" %str(exep)) + finally: self.connection.close() self.server_socket.close() @@ -46,5 +58,5 @@ def streaming(self): if __name__ == '__main__': # host, port - h, p = "192.168.1.100", 8000 + h, p = config.serverIP, 8000 VideoStreamingTest(h, p) diff --git a/test/ultrasonic_server_test.py b/test/ultrasonic_server_test.py index 9158c4fb..37a53500 100644 --- a/test/ultrasonic_server_test.py +++ b/test/ultrasonic_server_test.py @@ -1,5 +1,13 @@ __author__ = 'zhengwang' +import os, sys, inspect + +cd = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) +pd = os.path.dirname(cd) +sys.path.insert(0, pd) + +import config + import socket import time @@ -18,17 +26,19 @@ def __init__(self, host, port): def streaming(self): + print("Initializing") try: print("Host: ", self.host_name + ' ' + self.host_ip) print("Connection from: ", self.client_address) start = time.time() while True: - sensor_data = float(self.connection.recv(1024)) - print("Distance: %0.1f cm" % sensor_data) + sensor_data = (self.connection.recv(1024)) + print("Distance: %s cm" % sensor_data.decode()) # test for 10 seconds if time.time() - start > 10: + print("test timeout") break finally: self.connection.close() @@ -36,5 +46,5 @@ def streaming(self): if __name__ == '__main__': - h, p = "192.168.1.100", 8002 + h, p = config.serverIP, 8002 SensorStreamingTest(h, p)