├── BoardSetup.jpeg ├── Architecture.png ├── backend ├── package.json └── ttn.js ├── rpi ├── package.json └── run.js ├── README.md └── arduino └── Smartgate.ino /BoardSetup.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamariffikri/Smartgate/HEAD/BoardSetup.jpeg -------------------------------------------------------------------------------- /Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamariffikri/Smartgate/HEAD/Architecture.png -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": 3 | { 4 | "ttn": "1.3.1", 5 | "atob": "2.0.3" 6 | } 7 | } -------------------------------------------------------------------------------- /rpi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": 3 | { 4 | "serialport": "4.0.1", 5 | "raspicam": "0.2.13" 6 | } 7 | } -------------------------------------------------------------------------------- /backend/ttn.js: -------------------------------------------------------------------------------- 1 | 2 | var ttn = require('ttn'); 3 | var atob = require('atob'); 4 | 5 | var appEUI = ''; 6 | var accessKey = ''; 7 | var client = new ttn.Client('staging.thethingsnetwork.org', appEUI, accessKey); 8 | var devEUI = ''; 9 | 10 | client.on('connect', function() { 11 | console.log('connected') 12 | }) 13 | 14 | client.on('uplink', function (msg) { 15 | // These are messages sent by devices on The Things Network 16 | // msg = { 17 | // devEUI: '00000000973572D0', 18 | // fields: { /* ... */ }, 19 | // counter: 44, 20 | // metadata: { /* ... */ }, 21 | // } 22 | if(typeof msg.fields.raw !== "undefined") { 23 | var raw_msg = atob(msg.fields.raw); 24 | if( raw_msg.length > 4) { 25 | // get plate number 26 | var plateNumber = raw_msg;//new Buffer(msg.fields.raw, 'base64').toString()); 27 | console.log('Plate: '+raw_msg); 28 | // Send a downlink message to the device 29 | var payload = new Buffer('01', 'hex') 30 | client.downlink(devEUI, payload, '1h', 1) 31 | } 32 | } else 33 | if(typeof msg.fields !== "undefined") 34 | console.log(msg.fields); 35 | }) 36 | 37 | client.on('activation', function (evt) { 38 | // These are devices that are activated on The Things Network 39 | // evt = { 40 | // devEUI: '00000000973572D0', 41 | // } 42 | }) 43 | 44 | client.on('error', function (err) { 45 | // TODO: Handle error 46 | console.log(err); 47 | }) 48 | 49 | // End the client connection 50 | //client.end() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmartGate 2 | SmartGate, an IOT project using [OpenALPR](http://www.openalpr.com) Plate Recognition and LoRa in 3 | [Jomhack 2016, Cyberview](http://www.cyberjayamalaysia.com.my/happenings/2016/08/05/events/jomhack-smart-cities-with-lora). 4 | 5 | ## Architecture 6 | 7 | ![alt text](Architecture.png "Architecture") 8 | 9 | ## Demo 10 | 11 | https://www.instagram.com/p/BIzTmEuAIR_/ 12 | 13 | ## Hardware 14 | * [The Things Uno](https://shop.thethingsnetwork.com/index.php/product/the-things-uno/) 15 | * Light Sensor (or any other sensor; to detect incoming car) 16 | * Servo Motor 17 | * LCD 16x2 18 | * LED red & green 19 | * Rotaty Switch 20 | * Raspberry Pi 3 21 | * Camera module 22 | * 16GB SD card 23 | 24 | ## Installation 25 | 26 | ### Board Setup 27 | ![alt text](BoardSetup.jpeg "Board Setup") 28 | 29 | ### Arduino 30 | 31 | 1. Upload [the code](https://github.com/iamariffikri/Smartgate/blob/master/arduino/Smartgate.ino) 32 | 2. Update the `devAddr`, `nwkSKey` & `appSKey` gotten from TTN dashboard 33 | 34 | ### Raspberry Pi 35 | 36 | 1. [Install OpenALPR](https://gist.github.com/amstanley/9da7febc9a3e3c2228ee) 37 | 2. [Install NodeJS & NPM ](http://raspberrypi.stackexchange.com/a/37976) 38 | 3. Upload [the code](https://github.com/iamariffikri/Smartgate/blob/master/rpi) and run `npm install && node run.js` 39 | 40 | ### Backend 41 | 42 | 1. Copy [this code](https://github.com/iamariffikri/Smartgate/blob/master/backend) to a server or laptop 43 | 2. Update the `appEUI`, `accessKey` & `devEUI` and run `npm install && node ttn.js` 44 | 45 | ## License 46 | 47 | Licensed under the [MIT license](http://opensource.org/licenses/MIT) 48 | -------------------------------------------------------------------------------- /rpi/run.js: -------------------------------------------------------------------------------- 1 | 2 | var RaspiCam = require("raspicam"); 3 | var serialport = require('serialport'); 4 | 5 | serialport.list(function (err, ports) { 6 | ports.forEach(function(port) { 7 | console.log(port.comName); 8 | }); 9 | }); 10 | 11 | var SerialPort = serialport.SerialPort; 12 | var portName = process.argv[2]; 13 | var myPort = new SerialPort(portName, { 14 | 15 | baudRate: 9600, 16 | parser: serialport.parsers.readline("\r\n") 17 | }); 18 | 19 | myPort.on("open", onOpen); 20 | myPort.on("data", onData); 21 | 22 | function onOpen() { 23 | console.log("Open connection"); 24 | } 25 | 26 | function onData(data) { 27 | console.log("On Data: "+data); 28 | 29 | if(data == "action:takePicture") 30 | takePicture(); 31 | } 32 | 33 | function sendToSerial(data) { 34 | console.log("sending to serial: " + data); 35 | 36 | myPort.write(data); 37 | } 38 | 39 | function takePicture() { 40 | 41 | var output = "/home/pi/smart_gate/plate-"+new Date().getTime()+".jpg"; 42 | var camera = new RaspiCam({ 43 | mode: "photo", 44 | output: output, 45 | width: 2592, 46 | height: 1944, 47 | quality: "100", 48 | encoding: "jpg", 49 | timeout: 3000 50 | }); 51 | camera.start(); 52 | camera.on("read", function(err, timestamp, filename){ 53 | console.log("saved"); 54 | // todo // process image and send plate number 55 | if(filename.indexOf('~') < 0) 56 | processImage(output); 57 | }); 58 | } 59 | 60 | function sendPlateNumber(plateNumber) { 61 | sendToSerial(plateNumber); 62 | } 63 | 64 | function processImage(imagePath) { 65 | 66 | console.log('alpr -c sg ' + imagePath); 67 | const exec = require('child_process').exec; 68 | const child = exec('alpr -c sg ' + imagePath, (error, stdout, stderr) => { 69 | 70 | var str = `${stdout}`; 71 | var arr = str.split('\n'); 72 | 73 | if (arr.length > 9) { 74 | // success 75 | var goodResult = arr[1]; 76 | goodResult = goodResult.trim(); 77 | goodResult = goodResult.replace('-', ''); 78 | goodResult = goodResult.replace(' ', ''); 79 | goodResult = goodResult.replace('\t', ''); 80 | var values = goodResult.split('confidence:') 81 | console.log(values); 82 | console.log('plate found'); 83 | sendPlateNumber(values[0]); 84 | 85 | return values[0]; // this is the plate number 86 | } else { 87 | // not found 88 | console.log('not recognized'); 89 | sendToSerial('notRecognized'); 90 | return false; 91 | } 92 | 93 | /* 94 | console.log(`stdout: ${stdout}`); 95 | console.log(`stderr: ${stderr}`); 96 | console.log(`error: ${error}`); 97 | */ 98 | }); 99 | } 100 | -------------------------------------------------------------------------------- /arduino/Smartgate.ino: -------------------------------------------------------------------------------- 1 | #include "TheThingsUno.h" 2 | #include 3 | #include 4 | 5 | // Declare the pins 6 | int servoPin = 13; 7 | int pirPin = 8; 8 | int lightPin = 8; 9 | int greenLEDPin = 9; 10 | int redLEDPin = 10; 11 | int calibrationTime = 10; 12 | long unsigned int pause = 5000; 13 | long unsigned int lowIn; 14 | 15 | boolean lockLow = true; 16 | boolean takeLowTime; 17 | boolean isProcessingSerial; 18 | boolean isProcessingTTN; 19 | boolean isProcessingDownlink; 20 | 21 | // Create a servo object 22 | Servo servoEngine; 23 | LiquidCrystal lcd(12, 11, 5, 4, 3, 2); 24 | 25 | // Before your start, make sure that in the Tools menu, your Board and your 26 | // Port is set to Arduino Leonardo 27 | 28 | // After you registered your ABP device, go to The Things Network Dashboard 29 | // and copy the Device Addr, Network Session Key and App Session Key 30 | 31 | // Set your Device Address, for example: { 0x02, 0xDE, 0xAE, 0x00 }; 32 | const byte devAddr[4] = { ... }; 33 | 34 | // Set your Network Session Key, for example: { 0x2B, 0x7E, 0x15, 0x16, ... }; 35 | // This is used by the network to identify your device 36 | const byte nwkSKey[16] = { ... }; 37 | 38 | // Set your Application Session Key, for example: { 0x2B, 0x7E, 0x15, 0x16, ... }; 39 | // This is used by the network for encryption 40 | const byte appSKey[16] = { ... }; 41 | 42 | #define debugSerial Serial 43 | #define loraSerial Serial1 44 | 45 | #define debugPrintLn(...) { if (debugSerial) debugSerial.println(__VA_ARGS__); } 46 | #define debugPrint(...) { if (debugSerial) debugSerial.print(__VA_ARGS__); } 47 | 48 | TheThingsUno ttu; 49 | bool isLightsOut; 50 | bool isGateOpen = false; 51 | 52 | void setup() { 53 | // Set up the serial interfaces for the debugging serial monitor and LoRa module 54 | debugSerial.begin(115200); 55 | loraSerial.begin(57600); 56 | delay(1000); 57 | 58 | // Initialize and reset The Things Uno 59 | ttu.init(loraSerial, debugSerial); 60 | ttu.reset(); 61 | 62 | // Here we activate the device with your address and keys 63 | ttu.personalize(devAddr, nwkSKey, appSKey); 64 | 65 | // Show the status on the debugging serial monitor 66 | ttu.showStatus(); 67 | debugPrintLn("Setup for The Things Network complete"); 68 | 69 | // initialize pins for LED light 70 | pinMode(9, OUTPUT); 71 | pinMode(10, OUTPUT); 72 | digitalWrite(redLEDPin, HIGH); 73 | 74 | // initialize pins servo engine 75 | servoEngine.attach(servoPin); 76 | 77 | lcd.begin(16, 2); 78 | lcd.print("Setting up.."); 79 | } 80 | 81 | uint16_t getLight() { 82 | return analogRead(lightPin); 83 | } 84 | 85 | void switchGreen(bool greenLight) { 86 | if(greenLight) { 87 | digitalWrite(redLEDPin, LOW); 88 | digitalWrite(greenLEDPin, HIGH); 89 | debugPrintLn("PLATE MATCH!"); 90 | } else { 91 | digitalWrite(greenLEDPin, LOW); 92 | digitalWrite(redLEDPin, HIGH); 93 | } 94 | } 95 | 96 | void writeFirstRow(String displayString) { 97 | lcd.setCursor(0, 0); 98 | while (displayString.length() < 16) { 99 | displayString += " "; 100 | } 101 | 102 | lcd.print(displayString); 103 | } 104 | 105 | void writeSecondRow(String displayString) { 106 | lcd.setCursor(0, 1); 107 | while (displayString.length() < 16) { 108 | displayString += " "; 109 | } 110 | 111 | lcd.print(displayString); 112 | } 113 | 114 | void openGate(bool gateOpen) { 115 | if(gateOpen) { 116 | servoEngine.write(90); 117 | debugPrintLn("GATE OPENNING.."); 118 | } else { 119 | servoEngine.write(0); 120 | } 121 | isGateOpen = gateOpen; 122 | } 123 | 124 | void sendPlateNo(String plateNoDetected) { 125 | int bytes = ttu.sendString(plateNoDetected); 126 | } 127 | 128 | void readMotionSensor() { 129 | if(digitalRead(pirPin) == HIGH){ 130 | digitalWrite(redLEDPin, HIGH); //the led visualizes the sensors output pin state 131 | if(lockLow){ 132 | //makes sure we wait for a transition to LOW before any further output is made: 133 | lockLow = false; 134 | Serial.println("---"); 135 | Serial.print("motion detected at "); 136 | Serial.print(millis()/1000); 137 | Serial.println(" sec"); 138 | delay(50); 139 | } 140 | takeLowTime = true; 141 | } 142 | 143 | if(digitalRead(pirPin) == LOW){ 144 | digitalWrite(redLEDPin, LOW); //the led visualizes the sensors output pin state 145 | 146 | if(takeLowTime){ 147 | lowIn = millis(); //save the time of the transition from high to LOW 148 | takeLowTime = false; //make sure this is only done at the start of a LOW phase 149 | } 150 | //if the sensor is low for more than the given pause, 151 | //we assume that no more motion is going to happen 152 | if(!lockLow && millis() - lowIn > pause){ 153 | //makes sure this block of code is only executed again after 154 | //a new motion sequence has been detected 155 | lockLow = true; 156 | Serial.print("motion ended at "); //output 157 | Serial.print((millis() - pause)/1000); 158 | Serial.println(" sec"); 159 | delay(50); 160 | } 161 | } 162 | } 163 | 164 | void sendToSerial(String dataString) { 165 | // send to serial 166 | Serial.println(dataString); 167 | } 168 | 169 | String getPlateNumber() { 170 | // read from serial 171 | String plateNo = Serial.readString(); 172 | debugPrintLn("Plate Number Received =" + plateNo); 173 | return plateNo; 174 | } 175 | 176 | bool getGrantAccess() { 177 | // todo // read from serial 178 | int isValidNumber = Serial.read(); 179 | return isValidNumber; 180 | } 181 | 182 | void loop() { 183 | byte data[1]; 184 | int bytes = ttu.sendBytes(data, sizeof(data)); 185 | if (bytes > 0 && ttu.downlink[0] == 1) { 186 | switchGreen(true); 187 | openGate(true); 188 | writeFirstRow("Hello There!"); 189 | writeSecondRow("*CAR MATCHED*"); 190 | delay(4000); 191 | isProcessingSerial = isProcessingTTN = false; 192 | } else if(bytes > 0 && ttu.downlink[0] == 2) { 193 | writeFirstRow("NO ACCESS :("); 194 | writeSecondRow(" "); 195 | isProcessingSerial = isProcessingTTN = false; 196 | } else { 197 | switchGreen(false); 198 | openGate(false); 199 | } 200 | 201 | if(getLight() < 500 && !isProcessingSerial) { 202 | writeSecondRow("Car detected"); 203 | sendToSerial("action:takePicture"); 204 | isProcessingSerial = true; 205 | delay(1000); 206 | } else if(isProcessingSerial && !isProcessingTTN) { 207 | writeSecondRow("Reading.."); 208 | } else if(!isProcessingSerial && !isProcessingTTN) { 209 | writeFirstRow("Smartgate active"); 210 | writeSecondRow(" "); 211 | } 212 | 213 | // assuming pi only pass plate number 214 | if(Serial.available() > 0 && !isProcessingTTN) { 215 | isProcessingTTN = true; 216 | String plateNo = getPlateNumber(); 217 | 218 | if(plateNo.length() < 13) { 219 | writeFirstRow("Reading..."); 220 | writeSecondRow(plateNo); 221 | sendPlateNo(plateNo); 222 | } else { 223 | writeFirstRow("Sorry,"); 224 | writeSecondRow("*NO ACCESS*"); 225 | delay(4000); 226 | isProcessingSerial = isProcessingTTN = false; 227 | } 228 | } 229 | } 230 | --------------------------------------------------------------------------------