├── .gitignore ├── .npmignore ├── README.md ├── binding.gyp ├── index.js ├── install.sh ├── lib ├── amd64 │ └── ftd2xx.lib ├── ftd2xx.h └── i386 │ └── ftd2xx.lib ├── license ├── package.json ├── src ├── ftdi_constants.h ├── ftdi_device.cc ├── ftdi_device.h ├── ftdi_driver.cc └── ftdi_driver.h └── test ├── bnet9107Test.js ├── chw4000Test.js ├── fabi.js ├── ftdi.coffee ├── ftdi_unplug.cpp ├── multlipleDevicesTest.js ├── openBySerialTest.js ├── pollerTest.js └── pollerTest2.js /.gitignore: -------------------------------------------------------------------------------- 1 | $ cat .gitignore 2 | 3 | # Can ignore specific files 4 | .settings.xml 5 | .monitor 6 | .DS_Store 7 | 8 | # Use wildcards as well 9 | *~ 10 | #*.swp 11 | 12 | # Can also ignore all directories and files in a directory. 13 | node_modules 14 | node_modules/**/* 15 | build/* -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | test -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
  2 |   eeeee eeeee eeeee eeee       e  eeeee
  3 |   8   8 8  88 8   8 8          8  8   "
  4 |   8e  8 8   8 8e  8 8eee       8e 8eeee
  5 |   88  8 8   8 88  8 88      e  88    88
  6 |   88  8 8eee8 88ee8 88ee 88 8ee88 8ee88
  7 | 
  8 |   eeee eeeee eeeee e
  9 |   8      8   8   8 8
 10 |   8eee   8e  8e  8 8e
 11 |   88     88  88  8 88
 12 |   88     88  88ee8 88
 13 | 
14 | 15 | [![npm](https://img.shields.io/npm/v/ftdi.svg)](https://npmjs.org/package/ftdi) 16 | 17 | # Prerequisites: 18 | 19 | **Make sure you installed the ftdi driver: [ftdi](http://www.ftdichip.com/Drivers/D2XX.htm)** 20 | 21 | If you're are using a Linux distribution or Mac OS X you can run the **install.sh** script file to install the ftdi driver... 22 | For Windows the libs are shipped with this module. 23 | 24 | # Installation 25 | 26 | npm install ftdi 27 | 28 | This assumes you have everything on your system necessary to compile ANY native module for Node.js. This may not be the case, though, so please ensure the following are true for your system before filing an issue about "Does not install". For all operatings systems, please ensure you have Python 2.x installed AND not 3.0, [node-gyp](https://github.com/TooTallNate/node-gyp) (what we use to compile) requires Python 2.x. 29 | 30 | ### Windows: 31 | 32 | Ensure you have Visual Studio 2010 installed. If you have any version OTHER THAN VS 2010, please read this: https://github.com/TooTallNate/node-gyp/issues/44 33 | 34 | ### Mac OS X: 35 | 36 | Ensure that you have at a minimum the xCode Command Line Tools installed appropriate for your system configuration. If you recently upgrade OS, it probably removed your installation of Command Line Tools, please verify before submitting a ticket. 37 | 38 | Verify that it is installed by running ```xcode-select --install``` 39 | 40 | ### Linux: 41 | 42 | You know what you need for you system, basically your appropriate analog of build-essential. Keep rocking! 43 | 44 | If you the vendorId and productId are both zero you have to unload the driver before using your node app like this: 45 | ``` 46 | rmmod ftdi_sio 47 | rmmod usbserial 48 | ``` 49 | 50 | # Usage 51 | 52 | ## Listing or finding devices 53 | 54 | ```nodejs 55 | var ftdi = require('ftdi'); 56 | 57 | ftdi.find(0x27f4, 0x0203, function(err, devices) {}); // returns all ftdi devices with 58 | // matching vendor and product id 59 | ``` 60 | 61 | ## Create an FtdiDevice 62 | 63 | ```nodejs 64 | var ftdi = require('ftdi'); 65 | 66 | var device = new ftdi.FtdiDevice({ 67 | locationId: 0, 68 | serialNumber: 0 69 | }); 70 | 71 | // or 72 | var device = new ftdi.FtdiDevice(0); // index in list function 73 | ``` 74 | 75 | ## All together 76 | 77 | ```nodejs 78 | var ftdi = require('ftdi'); 79 | 80 | ftdi.find(0x27f4, 0x0203, function(err, devices) { 81 | var device = new ftdi.FtdiDevice(devices[0]); 82 | 83 | device.on('error', function(err) { 84 | }); 85 | 86 | device.open({ 87 | baudrate: 115200, 88 | databits: 8, 89 | stopbits: 1, 90 | parity: 'none', 91 | flowcontrol: 'none', // can be 'none', 'rts_cts', 'dtr_dsr', 'xon_xoff' 92 | // bitmode: 'cbus', // for bit bang 93 | // bitmask: 0xff // for bit bang 94 | }, 95 | function(err) { 96 | 97 | device.on('data', function(data) { 98 | 99 | }); 100 | 101 | device.write([0x01, 0x02, 0x03, 0x04, 0x05], function(err) { 102 | 103 | }); 104 | 105 | }); 106 | 107 | }); 108 | ``` 109 | 110 | ### Bit Bang infos 111 | bitmask: Is always a number (one byte). 112 | 113 | bitmode: Can be directly a number (one byte) like 0x20 or a string like 'cbus'. 114 | 115 | mapping: 116 | 117 | ```nodejs 118 | var bitmodes = { 119 | 'reset' : 0x00, 120 | 'async' : 0x01, 121 | 'mpsse' : 0x02, 122 | 'sync' : 0x04, 123 | 'mcu' : 0x0B, 124 | 'fast' : 0x10, 125 | 'cbus' : 0x20, 126 | 'single': 0x40 127 | }; 128 | 129 | /** 130 | * 0x00 = Reset 131 | * 0x01 = Asynchronous Bit Bang 132 | * 0x02 = MPSSE (FT2232, FT2232H, FT4232H and FT232H devices only) 133 | * 0x04 = Synchronous Bit Bang (FT232R, FT245R, FT2232, FT2232H, FT4232H and FT232H devices only) 134 | * 0x08 = MCU Host Bus Emulation Mode (FT2232, FT2232H, FT4232H and FT232H devices only) 135 | * 0x10 = Fast Opto-Isolated Serial Mode (FT2232, FT2232H, FT4232H and FT232H devices only) 136 | * 0x20 = CBUS Bit Bang Mode (FT232R and FT232H devices only) 137 | * 0x40 = Single Channel Synchronous 245 FIFO Mode (FT2232H and FT232H devices only) 138 | */ 139 | ``` 140 | 141 | # Troubleshoot 142 | 143 | ### Windows 144 | 145 | ### Mac OS X 146 | 147 | Error message: 148 | Can't open ftdi device: FT_DEVICE_NOT_OPENED 149 | 150 | Try to unload the Apple FTDI Driver by running the following command: 151 | 152 | ```sudo kextunload -b com.apple.driver.AppleUSBFTDI``` 153 | 154 | ### Linux 155 | 156 | # Release Notes 157 | 158 | ## v1.2.2 159 | 160 | - Fix wrong time.h header 161 | 162 | ## v1.2.0 163 | 164 | - Add support for node v4, v5 165 | 166 | ## v1.1.0 167 | 168 | - added bit bang support 169 | 170 | ## v1.0.3 171 | 172 | - revert "ready for node >= 0.11.4" 173 | 174 | ## v1.0.2 175 | 176 | - fix allocation/deallocation mismatch 177 | 178 | ## v1.0.1 179 | 180 | - ready for node >= 0.11.4 181 | 182 | ## v1.0.0 183 | 184 | - first release 185 | 186 | 187 | # License 188 | 189 | Copyright (c) 2014 Kaba AG 190 | 191 | Permission is hereby granted, free of charge, to any person obtaining a copy 192 | of this software and associated documentation files (the "Software"), to deal 193 | in the Software without restriction, including without limitation the rights 194 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 195 | copies of the Software, and to permit persons to whom the Software is 196 | furnished to do so, subject to the following conditions: 197 | 198 | The above copyright notice and this permission notice shall be included in 199 | all copies or substantial portions of the Software. 200 | 201 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 202 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 203 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 204 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 205 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 206 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 207 | THE SOFTWARE. 208 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'ftdi', 5 | 'sources': 6 | [ 7 | 'src/ftdi_device.cc', 8 | 'src/ftdi_driver.cc' 9 | ], 10 | 'include_dirs+': 11 | [ 12 | 'src/', 13 | " 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ftdi_constants.h" 11 | #include "ftdi_device.h" 12 | #include "ftdi_driver.h" 13 | 14 | #ifndef WIN32 15 | #include 16 | #include 17 | #else 18 | #include 19 | #endif 20 | 21 | using namespace std; 22 | using namespace v8; 23 | using namespace node; 24 | using namespace ftdi_device; 25 | 26 | 27 | /********************************** 28 | * Local defines 29 | **********************************/ 30 | #define EVENT_MASK (FT_EVENT_RXCHAR) 31 | #define WAIT_TIME_MILLISECONDS 250 32 | 33 | #define NANOSECS_PER_SECOND 1000000000 34 | #define NANOSECS_PER_MILISECOND 1000000 35 | #define MILISECS_PER_SECOND 1000 36 | 37 | #define JS_CLASS_NAME "FtdiDevice" 38 | #define JS_WRITE_FUNCTION "write" 39 | #define JS_OPEN_FUNCTION "open" 40 | #define JS_CLOSE_FUNCTION "close" 41 | 42 | 43 | /********************************** 44 | * Local Helper Functions protoypes 45 | **********************************/ 46 | UCHAR GetWordLength(int wordLength); 47 | UCHAR GetStopBits(int stopBits); 48 | UCHAR GetParity(const char* string); 49 | USHORT GetFlowControl(const char* string); 50 | 51 | void ToCString(Local val, char ** ptr); 52 | 53 | 54 | /********************************** 55 | * Local Variables 56 | **********************************/ 57 | 58 | 59 | /***************************** 60 | * CREATION Section 61 | *****************************/ 62 | 63 | FtdiDevice::FtdiDevice() 64 | { 65 | deviceState = DeviceState_Idle; 66 | uv_mutex_init(&closeMutex); 67 | }; 68 | 69 | FtdiDevice::~FtdiDevice() 70 | { 71 | if(connectParams.connectString != NULL) 72 | { 73 | delete connectParams.connectString; 74 | } 75 | }; 76 | 77 | NAN_METHOD(FtdiDevice::New) { 78 | Nan::HandleScope scope; 79 | 80 | Local locationId = Nan::New(DEVICE_LOCATION_ID_TAG).ToLocalChecked(); 81 | Local serial = Nan::New(DEVICE_SERIAL_NR_TAG).ToLocalChecked(); 82 | Local index = Nan::New(DEVICE_INDEX_TAG).ToLocalChecked(); 83 | Local description = Nan::New(DEVICE_DESCRIPTION_TAG).ToLocalChecked(); 84 | Local vid = Nan::New(DEVICE_VENDOR_ID_TAG).ToLocalChecked(); 85 | Local pid = Nan::New(DEVICE_PRODUCT_ID_TAG).ToLocalChecked(); 86 | 87 | FtdiDevice* object = new FtdiDevice(); 88 | 89 | // Check if the argument is an object 90 | if(info[0]->IsObject()) 91 | { 92 | Local obj = info[0]->ToObject(); 93 | 94 | /** 95 | * Determine how to connect to the device, we have the following possibilities: 96 | * 1) By location id 97 | * 2) By serial Number 98 | * 3) By description 99 | * 4) By index 100 | * In this order we check for availability of the parameter and the first valid we found is taken 101 | */ 102 | if(obj->Has(locationId) && obj->Get(locationId)->Int32Value() != 0) 103 | { 104 | object->connectParams.connectId = obj->Get(locationId)->Int32Value(); 105 | object->connectParams.connectType = ConnectType_ByLocationId; 106 | } 107 | else if(obj->Has(serial) && obj->Get(serial)->ToString()->Length() > 0) 108 | { 109 | ToCString(obj->Get(serial)->ToString(), &object->connectParams.connectString); 110 | object->connectParams.connectType = ConnectType_BySerial; 111 | } 112 | else if(obj->Has(description) && obj->Get(description)->ToString()->Length() > 0) 113 | { 114 | ToCString(obj->Get(description)->ToString(), &object->connectParams.connectString); 115 | object->connectParams.connectType = ConnectType_ByDescription; 116 | } 117 | else if(obj->Has(index)) 118 | { 119 | object->connectParams.connectId = obj->Get(index)->Int32Value(); 120 | object->connectParams.connectType = ConnectType_ByIndex; 121 | } 122 | object->connectParams.vid = 0; 123 | if(obj->Has(vid)) 124 | { 125 | object->connectParams.vid = obj->Get(vid)->Int32Value(); 126 | } 127 | object->connectParams.pid = 0; 128 | if(obj->Has(pid)) 129 | { 130 | object->connectParams.pid = obj->Get(pid)->Int32Value(); 131 | } 132 | } 133 | // if the argument is a number we connect by index to the device 134 | else if(info[0]->IsNumber()) 135 | { 136 | object->connectParams.connectId = (int) info[0]->NumberValue(); 137 | object->connectParams.connectType = ConnectType_ByIndex; 138 | } 139 | else 140 | { 141 | return Nan::ThrowTypeError("new expects a object as argument"); 142 | } 143 | 144 | object->Wrap(info.This()); 145 | 146 | info.GetReturnValue().Set(info.This()); 147 | } 148 | 149 | /***************************** 150 | * READ Section 151 | *****************************/ 152 | class ReadWorker : public Nan::AsyncWorker { 153 | public: 154 | ReadWorker(Nan::Callback *callback, FtdiDevice* device) 155 | : Nan::AsyncWorker(callback), device(device) {} 156 | ~ReadWorker() {} 157 | 158 | // Executed inside the worker-thread. 159 | // It is not safe to access V8, or V8 data structures 160 | // here, so everything we need for input and output 161 | // should go on `this`. 162 | void Execute () { 163 | baton = &batonEntity; 164 | baton->length = 0; 165 | baton->data = NULL; 166 | status = FtdiDevice::ReadDataAsync(device, baton); 167 | } 168 | 169 | void WorkComplete () { 170 | Nan::HandleScope scope; 171 | 172 | if (ErrorMessage() == NULL) 173 | HandleOKCallback(); 174 | else 175 | HandleErrorCallback(); 176 | } 177 | 178 | // Executed when the async work is complete 179 | // this function will be run inside the main event loop 180 | // so it is safe to use V8 again 181 | void HandleOKCallback () { 182 | Nan::HandleScope scope; 183 | 184 | // Execute the callback in case we have one and we have read some data 185 | if(status != FT_OK || (baton->length != 0)) 186 | { 187 | Local argv[2]; 188 | 189 | Local slowBuffer = Nan::CopyBuffer((char*)baton->data, baton->length).ToLocalChecked(); 190 | Local globalObj = Nan::GetCurrentContext()->Global(); 191 | Local bufferConstructor = Local::Cast(globalObj->Get(Nan::New("Buffer").ToLocalChecked())); 192 | Handle constructorArgs[3] = { slowBuffer, Nan::New(baton->length), Nan::New(0) }; 193 | Local actualBuffer = bufferConstructor->NewInstance(3, constructorArgs); 194 | argv[1] = actualBuffer; 195 | 196 | if(status != FT_OK) 197 | { 198 | argv[0] = Nan::New(GetStatusString(status)).ToLocalChecked(); 199 | } 200 | else 201 | { 202 | argv[0] = Local(Nan::Undefined()); 203 | } 204 | 205 | callback->Call(2, argv); 206 | } 207 | 208 | if(baton->data != NULL) 209 | { 210 | delete baton->data; 211 | baton->length = 0; 212 | } 213 | 214 | // Check if we want close the device 215 | if(device->deviceState == DeviceState_Closing) 216 | { 217 | // Signal that we have stoped reading 218 | uv_mutex_unlock(&device->closeMutex); 219 | } 220 | else 221 | { 222 | // Restart reading loop 223 | AsyncQueueWorkerPersistent(this); 224 | } 225 | 226 | }; 227 | 228 | private: 229 | FT_STATUS status; 230 | FtdiDevice* device; 231 | ReadBaton_t* baton; 232 | ReadBaton_t batonEntity; 233 | }; 234 | 235 | FT_STATUS FtdiDevice::ReadDataAsync(FtdiDevice* device, ReadBaton_t* baton) 236 | { 237 | FT_STATUS ftStatus; 238 | DWORD RxBytes; 239 | DWORD BytesReceived; 240 | 241 | // start permanent read loop 242 | while(1) 243 | { 244 | // Wait for data or close event 245 | device->WaitForReadOrCloseEvent(); 246 | 247 | // Check Queue Status 248 | uv_mutex_lock(&libraryMutex); 249 | ftStatus = FT_GetQueueStatus(device->ftHandle, &RxBytes); 250 | uv_mutex_unlock(&libraryMutex); 251 | 252 | if(ftStatus != FT_OK) 253 | { 254 | fprintf(stderr, "Can't get queue status from ftdi device: %s\n", error_strings[ftStatus]); 255 | return ftStatus; 256 | } 257 | 258 | // Read available data if any 259 | if(RxBytes > 0) 260 | { 261 | baton->data = new uint8_t[RxBytes]; 262 | 263 | uv_mutex_lock(&libraryMutex); 264 | ftStatus = FT_Read(device->ftHandle, baton->data, RxBytes, &BytesReceived); 265 | uv_mutex_unlock(&libraryMutex); 266 | if (ftStatus != FT_OK) 267 | { 268 | fprintf(stderr, "Can't read from ftdi device: %s\n", error_strings[ftStatus]); 269 | } 270 | 271 | baton->length = BytesReceived; 272 | return ftStatus; 273 | } 274 | 275 | // Check if we are closing the device 276 | if(device->deviceState == DeviceState_Closing) 277 | { 278 | uv_mutex_lock(&libraryMutex); 279 | FT_Purge(device->ftHandle, FT_PURGE_RX | FT_PURGE_TX); 280 | uv_mutex_unlock(&libraryMutex); 281 | return ftStatus; 282 | } 283 | } 284 | } 285 | 286 | 287 | /***************************** 288 | * OPEN Section 289 | *****************************/ 290 | class OpenWorker : public Nan::AsyncWorker { 291 | public: 292 | OpenWorker(Nan::Callback *callback, FtdiDevice* device, Nan::Callback *callback_read) 293 | : Nan::AsyncWorker(callback), device(device), callback_read(callback_read) {} 294 | ~OpenWorker() {} 295 | 296 | // Executed inside the worker-thread. 297 | // It is not safe to access V8, or V8 data structures 298 | // here, so everything we need for input and output 299 | // should go on `this`. 300 | void Execute () { 301 | status = FtdiDevice::OpenAsync(device, callback_read); 302 | } 303 | 304 | // Executed when the async work is complete 305 | // this function will be run inside the main event loop 306 | // so it is safe to use V8 again 307 | void HandleOKCallback () { 308 | Nan::HandleScope scope; 309 | 310 | /** 311 | * If the open process was sucessful, we start the read thread 312 | * which waits for data events sents from the device. 313 | */ 314 | if(status == FT_OK) 315 | { 316 | status = device->PrepareAsyncRead(); 317 | if(status == FT_OK) 318 | { 319 | // Lock the Close mutex (it is needed to signal when async read has stoped reading) 320 | uv_mutex_lock(&device->closeMutex); 321 | 322 | AsyncQueueWorkerPersistent(new ReadWorker(callback_read, device)); 323 | } 324 | // In case read thread could not be started, dispose the callback 325 | else 326 | { 327 | fprintf(stderr, "Could not Initialise event notification: %s\n", error_strings[status]); 328 | } 329 | } 330 | 331 | // Check for callback function 332 | if(callback != NULL) 333 | { 334 | Local argv[1]; 335 | if(status != FT_OK) 336 | { 337 | argv[0] = Nan::New(GetStatusString(status)).ToLocalChecked(); 338 | } 339 | else 340 | { 341 | argv[0] = Local(Nan::Undefined()); 342 | } 343 | 344 | callback->Call(1, argv); 345 | } 346 | }; 347 | 348 | private: 349 | FT_STATUS status; 350 | FtdiDevice* device; 351 | Nan::Callback *callback_read; 352 | }; 353 | 354 | NAN_METHOD(FtdiDevice::Open) { 355 | Nan::HandleScope scope; 356 | 357 | Nan::Callback *callback = NULL; 358 | Nan::Callback *callback_read = NULL; 359 | 360 | // Get Device Object 361 | FtdiDevice* device = Nan::ObjectWrap::Unwrap(info.This()); 362 | if(device == NULL) 363 | { 364 | return Nan::ThrowError("No FtdiDevice object found in Java Script object"); 365 | } 366 | 367 | if (info.Length() != 3) 368 | { 369 | return Nan::ThrowError("open() expects three arguments"); 370 | } 371 | 372 | if (!info[0]->IsObject()) 373 | { 374 | return Nan::ThrowError("open() expects a object as first argument"); 375 | } 376 | 377 | // options 378 | if(!info[1]->IsFunction()) 379 | { 380 | return Nan::ThrowError("open() expects a function (openFisnished) as second argument"); 381 | } 382 | Local readCallback = info[1]; 383 | 384 | // options 385 | if(!info[2]->IsFunction()) 386 | { 387 | return Nan::ThrowError("open() expects a function (readFinsihed) as third argument"); 388 | } 389 | Local openCallback = info[2]; 390 | 391 | 392 | // Check if device is not already open or opening 393 | if(device->deviceState == DeviceState_Open) 394 | { 395 | Local argv[1]; 396 | argv[0] = Nan::New(FT_STATUS_CUSTOM_ALREADY_OPEN).ToLocalChecked(); 397 | callback = new Nan::Callback(info[1].As()); 398 | callback->Call(1, argv); 399 | } 400 | else 401 | { 402 | device->deviceState = DeviceState_Open; 403 | 404 | // Extract the connection parameters 405 | device->ExtractDeviceSettings(info[0]->ToObject()); 406 | 407 | callback_read = new Nan::Callback(readCallback.As()); 408 | callback = new Nan::Callback(openCallback.As()); 409 | 410 | Nan::AsyncQueueWorker(new OpenWorker(callback, device, callback_read)); 411 | } 412 | 413 | return; 414 | } 415 | 416 | FT_STATUS FtdiDevice::OpenAsync(FtdiDevice* device, Nan::Callback *callback_read) 417 | { 418 | FT_STATUS ftStatus; 419 | 420 | ftStatus = device->OpenDevice(); 421 | if (ftStatus != FT_OK) 422 | { 423 | fprintf(stderr, "Can't open ftdi device: %s\n", error_strings[ftStatus]); 424 | } 425 | else 426 | { 427 | ftStatus = device->SetDeviceSettings(); 428 | if (ftStatus != FT_OK) 429 | { 430 | fprintf(stderr, "Can't Set DeviceSettings: %s\n", error_strings[ftStatus]); 431 | } 432 | } 433 | 434 | return ftStatus; 435 | } 436 | 437 | FT_STATUS FtdiDevice::OpenDevice() 438 | { 439 | FT_STATUS status = FT_OK; 440 | 441 | // For open by Index case 442 | if(connectParams.connectType == ConnectType_ByIndex) 443 | { 444 | uv_mutex_lock(&libraryMutex); 445 | #ifndef WIN32 446 | // In case of Linux / Mac we have to set the VID PID of the 447 | // device we want to connect to 448 | status = FT_SetVIDPID(connectParams.vid, connectParams.pid); 449 | #endif 450 | if(status == FT_OK) 451 | { 452 | status = FT_Open(connectParams.connectId, &ftHandle); 453 | } 454 | uv_mutex_unlock(&libraryMutex); 455 | return status; 456 | } 457 | // other cases 458 | else 459 | { 460 | PVOID arg; 461 | DWORD flags; 462 | 463 | switch(connectParams.connectType) 464 | { 465 | case ConnectType_ByDescription: 466 | { 467 | arg = (PVOID) connectParams.connectString; 468 | flags = FT_OPEN_BY_DESCRIPTION; 469 | // printf("OpenDevice [Flag: %d, Arg: %s]\r\n", flags, (char *)arg); 470 | } 471 | break; 472 | 473 | case ConnectType_BySerial: 474 | { 475 | arg = (PVOID) connectParams.connectString; 476 | flags = FT_OPEN_BY_SERIAL_NUMBER; 477 | // printf("OpenDevice [Flag: %d, Arg: %s]\r\n", flags, (char *)arg); 478 | } 479 | break; 480 | 481 | 482 | case ConnectType_ByLocationId: 483 | { 484 | arg = (PVOID) connectParams.connectId; 485 | flags = FT_OPEN_BY_LOCATION; 486 | // printf("OpenDevice [Flag: %d, Arg: %d]\r\n", flags, connectParams.connectId); 487 | } 488 | break; 489 | 490 | default: 491 | { 492 | return FT_INVALID_PARAMETER; 493 | } 494 | } 495 | 496 | uv_mutex_lock(&vidPidMutex); 497 | uv_mutex_lock(&libraryMutex); 498 | 499 | #ifndef WIN32 500 | // In case of Linux / Mac we have to set the VID PID of the 501 | // device we want to connect to 502 | status = FT_SetVIDPID(connectParams.vid, connectParams.pid); 503 | #endif 504 | if(status == FT_OK) 505 | { 506 | status = FT_OpenEx(arg, flags, &ftHandle); 507 | } 508 | uv_mutex_unlock(&libraryMutex); 509 | uv_mutex_unlock(&vidPidMutex); 510 | return status; 511 | } 512 | 513 | return FT_INVALID_PARAMETER; 514 | } 515 | 516 | 517 | /***************************** 518 | * WRITE Section 519 | *****************************/ 520 | class WriteWorker : public Nan::AsyncWorker { 521 | public: 522 | WriteWorker(Nan::Callback *callback, FtdiDevice* device, WriteBaton_t* baton) 523 | : Nan::AsyncWorker(callback), device(device), baton(baton) {} 524 | ~WriteWorker() {} 525 | 526 | // Executed inside the worker-thread. 527 | // It is not safe to access V8, or V8 data structures 528 | // here, so everything we need for input and output 529 | // should go on `this`. 530 | void Execute () { 531 | status = FtdiDevice::WriteAsync(device, baton); 532 | } 533 | 534 | // Executed when the async work is complete 535 | // this function will be run inside the main event loop 536 | // so it is safe to use V8 again 537 | void HandleOKCallback () { 538 | Nan::HandleScope scope; 539 | 540 | if(callback != NULL) 541 | { 542 | Local argv[1]; 543 | if(status != FT_OK) 544 | { 545 | argv[0] = Nan::New(GetStatusString(status)).ToLocalChecked(); 546 | } 547 | else 548 | { 549 | argv[0] = Local(Nan::Undefined()); 550 | } 551 | 552 | callback->Call(1, argv); 553 | } 554 | 555 | delete baton->data; 556 | delete baton; 557 | }; 558 | 559 | private: 560 | FT_STATUS status; 561 | FtdiDevice* device; 562 | WriteBaton_t* baton; 563 | }; 564 | 565 | NAN_METHOD(FtdiDevice::Write) { 566 | Nan::HandleScope scope; 567 | 568 | Nan::Callback *callback = NULL; 569 | 570 | // buffer 571 | if(!info[0]->IsObject() || !Buffer::HasInstance(info[0])) 572 | { 573 | return Nan::ThrowError("First argument must be a buffer"); 574 | } 575 | Local buffer = info[0]->ToObject(); 576 | 577 | // Obtain Device Object 578 | FtdiDevice* device = Nan::ObjectWrap::Unwrap(info.This()); 579 | if(device == NULL) 580 | { 581 | return Nan::ThrowError("No FtdiDevice object found in Java Script object"); 582 | } 583 | 584 | Local writeCallback; 585 | // options 586 | if(info.Length() > 1 && info[1]->IsFunction()) 587 | { 588 | writeCallback = info[1]; 589 | callback = new Nan::Callback(writeCallback.As()); 590 | } 591 | 592 | WriteBaton_t* baton = new WriteBaton_t(); 593 | baton->length = (DWORD)Buffer::Length(buffer); 594 | baton->data = new uint8_t[baton->length]; 595 | memcpy(baton->data, Buffer::Data(buffer), baton->length); 596 | 597 | Nan::AsyncQueueWorker(new WriteWorker(callback, device, baton)); 598 | 599 | return; 600 | } 601 | 602 | FT_STATUS FtdiDevice::WriteAsync(FtdiDevice* device, WriteBaton_t* baton) 603 | { 604 | FT_STATUS ftStatus; 605 | DWORD bytesWritten; 606 | 607 | uv_mutex_lock(&libraryMutex); 608 | ftStatus = FT_Write(device->ftHandle, baton->data, baton->length, &bytesWritten); 609 | uv_mutex_unlock(&libraryMutex); 610 | 611 | return ftStatus; 612 | } 613 | 614 | /***************************** 615 | * CLOSE Section 616 | *****************************/ 617 | class CloseWorker : public Nan::AsyncWorker { 618 | public: 619 | CloseWorker(Nan::Callback *callback, FtdiDevice* device) 620 | : Nan::AsyncWorker(callback), device(device) {} 621 | ~CloseWorker() {} 622 | 623 | // Executed inside the worker-thread. 624 | // It is not safe to access V8, or V8 data structures 625 | // here, so everything we need for input and output 626 | // should go on `this`. 627 | void Execute () { 628 | status = FtdiDevice::CloseAsync(device); 629 | } 630 | 631 | // Executed when the async work is complete 632 | // this function will be run inside the main event loop 633 | // so it is safe to use V8 again 634 | void HandleOKCallback () { 635 | Nan::HandleScope scope; 636 | 637 | device->deviceState = DeviceState_Idle; 638 | 639 | if(callback != NULL) 640 | { 641 | Local argv[1]; 642 | if(status != FT_OK) 643 | { 644 | argv[0] = Nan::New(GetStatusString(status)).ToLocalChecked(); 645 | } 646 | else 647 | { 648 | argv[0] = Local(Nan::Undefined()); 649 | } 650 | 651 | callback->Call(1, argv); 652 | } 653 | }; 654 | 655 | private: 656 | FT_STATUS status; 657 | FtdiDevice* device; 658 | }; 659 | 660 | NAN_METHOD(FtdiDevice::Close) { 661 | Nan::HandleScope scope; 662 | 663 | Nan::Callback *callback = NULL; 664 | 665 | // Obtain Device Object 666 | FtdiDevice* device = Nan::ObjectWrap::Unwrap(info.This()); 667 | if(device == NULL) 668 | { 669 | return Nan::ThrowError("No FtdiDevice object found in Java Script object"); 670 | } 671 | 672 | // Check the device state 673 | if(device->deviceState != DeviceState_Open) 674 | { 675 | // callback 676 | if(info[0]->IsFunction()) 677 | { 678 | Local argv[1]; 679 | argv[0] = Nan::New(FT_STATUS_CUSTOM_ALREADY_CLOSING).ToLocalChecked(); 680 | callback = new Nan::Callback(info[0].As()); 681 | callback->Call(1, argv); 682 | } 683 | } 684 | else 685 | { 686 | // Set Device State 687 | device->deviceState = DeviceState_Closing; 688 | 689 | // callback 690 | if(info[0]->IsFunction()) 691 | { 692 | callback = new Nan::Callback(info[0].As()); 693 | } 694 | 695 | Nan::AsyncQueueWorker(new CloseWorker(callback, device)); 696 | } 697 | 698 | return; 699 | } 700 | 701 | FT_STATUS FtdiDevice::CloseAsync(FtdiDevice* device) 702 | { 703 | FT_STATUS ftStatus; 704 | 705 | // Send Event for Read Loop 706 | device->SignalCloseEvent(); 707 | 708 | // Wait till read loop finishes 709 | uv_mutex_lock(&device->closeMutex); 710 | uv_mutex_unlock(&device->closeMutex); 711 | 712 | // Close the device 713 | uv_mutex_lock(&libraryMutex); 714 | ftStatus = FT_Close(device->ftHandle); 715 | uv_mutex_unlock(&libraryMutex); 716 | 717 | return ftStatus; 718 | } 719 | 720 | 721 | /***************************** 722 | * Helper Section 723 | *****************************/ 724 | FT_STATUS FtdiDevice::SetDeviceSettings() 725 | { 726 | FT_STATUS ftStatus; 727 | 728 | uv_mutex_lock(&libraryMutex); 729 | ftStatus = FT_SetDataCharacteristics(ftHandle, deviceParams.wordLength, deviceParams.stopBits, deviceParams.parity); 730 | uv_mutex_unlock(&libraryMutex); 731 | if (ftStatus != FT_OK) 732 | { 733 | fprintf(stderr, "Can't Set FT_SetDataCharacteristics: %s\n", error_strings[ftStatus]); 734 | return ftStatus; 735 | } 736 | 737 | uv_mutex_lock(&libraryMutex); 738 | ftStatus = FT_SetBaudRate(ftHandle, deviceParams.baudRate); 739 | uv_mutex_unlock(&libraryMutex); 740 | if (ftStatus != FT_OK) 741 | { 742 | fprintf(stderr, "Can't setBaudRate: %s\n", error_strings[ftStatus]); 743 | return ftStatus; 744 | } 745 | 746 | uv_mutex_lock(&libraryMutex); 747 | ftStatus = FT_SetFlowControl(ftHandle, deviceParams.fc, 0x11, 0x13); 748 | uv_mutex_unlock(&libraryMutex); 749 | if (ftStatus != FT_OK) 750 | { 751 | fprintf(stderr, "Can't set flow control: %s\n", error_strings[ftStatus]); 752 | return ftStatus; 753 | } 754 | 755 | if (deviceParams.hasBitSettings == true) { 756 | uv_mutex_lock(&libraryMutex); 757 | ftStatus = FT_SetBitMode(ftHandle, deviceParams.bitMask, deviceParams.bitMode); 758 | uv_mutex_unlock(&libraryMutex); 759 | if (ftStatus != FT_OK) 760 | { 761 | fprintf(stderr, "Can't setBitMode: %s\n", error_strings[ftStatus]); 762 | return ftStatus; 763 | } 764 | } 765 | 766 | // printf("Connection Settings set [Baud: %d, DataBits: %d, StopBits: %d, Parity: %d]\r\n", deviceParams.baudRate, deviceParams.wordLength, deviceParams.stopBits, deviceParams.parity); 767 | return ftStatus; 768 | } 769 | 770 | void FtdiDevice::ExtractDeviceSettings(Local options) 771 | { 772 | Nan::EscapableHandleScope scope; 773 | Local baudrate = Nan::New(CONNECTION_BAUDRATE_TAG).ToLocalChecked(); 774 | Local databits = Nan::New(CONNECTION_DATABITS_TAG).ToLocalChecked(); 775 | Local stopbits = Nan::New(CONNECTION_STOPBITS_TAG).ToLocalChecked(); 776 | Local parity = Nan::New(CONNECTION_PARITY_TAG).ToLocalChecked(); 777 | Local flowctrl = Nan::New(CONNECTION_FC_TAG).ToLocalChecked(); 778 | Local bitmode = Nan::New(CONNECTION_BITMODE).ToLocalChecked(); 779 | Local bitmask = Nan::New(CONNECTION_BITMASK).ToLocalChecked(); 780 | 781 | if(options->Has(baudrate)) 782 | { 783 | deviceParams.baudRate = options->Get(baudrate)->ToInt32()->Int32Value(); 784 | } 785 | if(options->Has(databits)) 786 | { 787 | deviceParams.wordLength = GetWordLength(options->Get(databits)->ToInt32()->Int32Value()); 788 | } 789 | if(options->Has(stopbits)) 790 | { 791 | deviceParams.stopBits = GetStopBits(options->Get(stopbits)->ToInt32()->Int32Value()); 792 | } 793 | if(options->Has(parity)) 794 | { 795 | char* str; 796 | ToCString(options->Get(parity)->ToString(), &str); 797 | deviceParams.parity = GetParity(str); 798 | delete[] str; 799 | } 800 | if(options->Has(flowctrl)) 801 | { 802 | char* str; 803 | ToCString(options->Get(flowctrl)->ToString(), &str); 804 | deviceParams.fc = GetFlowControl(str); 805 | delete[] str; 806 | } 807 | 808 | bool hasBitSettings = false; 809 | deviceParams.bitMode = 0; 810 | deviceParams.bitMask = 0; 811 | 812 | if(options->Has(bitmode)) 813 | { 814 | deviceParams.bitMode = options->Get(bitmode)->ToInt32()->Int32Value(); 815 | hasBitSettings = true; 816 | } else { 817 | hasBitSettings = false; 818 | } 819 | 820 | if(hasBitSettings && options->Has(bitmask)) 821 | { 822 | deviceParams.bitMask = options->Get(bitmask)->ToInt32()->Int32Value(); 823 | hasBitSettings = true; 824 | } 825 | 826 | deviceParams.hasBitSettings = hasBitSettings; 827 | } 828 | 829 | UCHAR GetWordLength(int wordLength) 830 | { 831 | switch(wordLength) 832 | { 833 | case 7: 834 | return FT_BITS_7; 835 | 836 | case 8: 837 | default: 838 | return FT_BITS_8; 839 | } 840 | } 841 | 842 | UCHAR GetStopBits(int stopBits) 843 | { 844 | switch(stopBits) 845 | { 846 | case 1: 847 | return FT_STOP_BITS_1; 848 | 849 | case 2: 850 | default: 851 | return FT_STOP_BITS_2; 852 | } 853 | } 854 | 855 | UCHAR GetParity(const char* string) 856 | { 857 | if(strcmp(CONNECTION_PARITY_NONE, string) == 0) 858 | { 859 | return FT_PARITY_NONE; 860 | } 861 | else if(strcmp(CONNECTION_PARITY_ODD, string) == 0) 862 | { 863 | return FT_PARITY_ODD; 864 | } 865 | else if(strcmp(CONNECTION_PARITY_EVEN, string) == 0) 866 | { 867 | return FT_PARITY_EVEN; 868 | } 869 | return FT_PARITY_NONE; 870 | } 871 | 872 | USHORT GetFlowControl(const char* string) 873 | { 874 | if(strcmp(CONNECTION_FC_NONE, string) == 0) 875 | { 876 | return FT_FLOW_NONE; 877 | } 878 | else if(strcmp(CONNECTION_FC_RTS_CTS, string) == 0) 879 | { 880 | return FT_FLOW_RTS_CTS; 881 | } 882 | else if(strcmp(CONNECTION_FC_DTR_DSR, string) == 0) 883 | { 884 | return FT_FLOW_DTR_DSR; 885 | } 886 | else if(strcmp(CONNECTION_FC_XON_XOFF, string) == 0) 887 | { 888 | return FT_FLOW_XON_XOFF; 889 | } 890 | return FT_FLOW_NONE; 891 | } 892 | 893 | /** 894 | * Generates a new C String. It allocates memory for the new 895 | * string. Be sure to free the memory as soon as you dont need 896 | * it anymore. 897 | */ 898 | void ToCString(Local val, char ** ptr) 899 | { 900 | *ptr = new char[val->Utf8Length() + 1]; 901 | #if (NODE_MODULE_VERSION > 0x000B) 902 | // Node 0.11+ (0.11.3 and below won't compile with these) 903 | val->WriteOneByte(reinterpret_cast(*ptr), 0, -1, 0); 904 | #else 905 | // Node 0.8 and 0.10 906 | val->WriteAscii(*ptr, 0, -1, 0); 907 | #endif 908 | } 909 | 910 | 911 | /***************************** 912 | * Platform dependet Section 913 | *****************************/ 914 | #ifndef WIN32 915 | FT_STATUS FtdiDevice::PrepareAsyncRead() 916 | { 917 | FT_STATUS status; 918 | pthread_mutex_init(&dataEventHandle.eMutex, NULL); 919 | pthread_cond_init(&dataEventHandle.eCondVar, NULL); 920 | uv_mutex_lock(&libraryMutex); 921 | status = FT_SetEventNotification(ftHandle, EVENT_MASK, (PVOID) &dataEventHandle); 922 | uv_mutex_unlock(&libraryMutex); 923 | return status; 924 | } 925 | 926 | void FtdiDevice::WaitForReadOrCloseEvent() 927 | { 928 | DWORD rxBytes = 0; 929 | 930 | pthread_mutex_lock(&dataEventHandle.eMutex); 931 | 932 | // Only sleep in case there is nothing to do 933 | if(FT_GetQueueStatus(ftHandle, &rxBytes) == FT_OK && deviceState != DeviceState_Closing && rxBytes == 0) 934 | { 935 | struct timespec ts; 936 | struct timeval tp; 937 | 938 | gettimeofday(&tp, NULL); 939 | 940 | int additionalSeconds = 0; 941 | int additionalMilisecs = 0; 942 | additionalSeconds = (WAIT_TIME_MILLISECONDS / MILISECS_PER_SECOND); 943 | additionalMilisecs = (WAIT_TIME_MILLISECONDS % MILISECS_PER_SECOND); 944 | 945 | long additionalNanosec = tp.tv_usec * 1000 + additionalMilisecs * NANOSECS_PER_MILISECOND; 946 | additionalSeconds += (additionalNanosec / NANOSECS_PER_SECOND); 947 | additionalNanosec = (additionalNanosec % NANOSECS_PER_SECOND); 948 | 949 | /* Convert from timeval to timespec */ 950 | ts.tv_sec = tp.tv_sec; 951 | ts.tv_nsec = additionalNanosec; 952 | ts.tv_sec += additionalSeconds; 953 | 954 | pthread_cond_timedwait(&dataEventHandle.eCondVar, &dataEventHandle.eMutex, &ts); 955 | } 956 | pthread_mutex_unlock(&dataEventHandle.eMutex); 957 | } 958 | 959 | void FtdiDevice::SignalCloseEvent() 960 | { 961 | pthread_mutex_lock(&dataEventHandle.eMutex); 962 | pthread_cond_signal(&dataEventHandle.eCondVar); 963 | pthread_mutex_unlock(&dataEventHandle.eMutex); 964 | } 965 | 966 | #else 967 | 968 | FT_STATUS FtdiDevice::PrepareAsyncRead() 969 | { 970 | FT_STATUS status; 971 | dataEventHandle = CreateEvent(NULL, false /* auto-reset event */, false /* non-signalled state */, ""); 972 | uv_mutex_lock(&libraryMutex); 973 | status = FT_SetEventNotification(ftHandle, EVENT_MASK, dataEventHandle); 974 | uv_mutex_unlock(&libraryMutex); 975 | return status; 976 | } 977 | 978 | void FtdiDevice::WaitForReadOrCloseEvent() 979 | { 980 | WaitForSingleObject(dataEventHandle, WAIT_TIME_MILLISECONDS); 981 | } 982 | 983 | void FtdiDevice::SignalCloseEvent() 984 | { 985 | SetEvent(dataEventHandle); 986 | } 987 | #endif 988 | 989 | void FtdiDevice::Initialize(v8::Handle target) 990 | { 991 | // Prepare constructor template 992 | Local tpl = Nan::New(New); 993 | tpl->SetClassName(Nan::New(JS_CLASS_NAME).ToLocalChecked()); 994 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 995 | // Prototype 996 | tpl->PrototypeTemplate()->Set(Nan::New(JS_WRITE_FUNCTION).ToLocalChecked(), Nan::New(Write)); 997 | tpl->PrototypeTemplate()->Set(Nan::New(JS_OPEN_FUNCTION).ToLocalChecked(), Nan::New(Open)); 998 | tpl->PrototypeTemplate()->Set(Nan::New(JS_CLOSE_FUNCTION).ToLocalChecked(), Nan::New(Close)); 999 | 1000 | Local constructor = tpl->GetFunction(); 1001 | target->Set(Nan::New(JS_CLASS_NAME).ToLocalChecked(), constructor); 1002 | } 1003 | 1004 | extern "C" 1005 | { 1006 | void init (v8::Handle target) 1007 | { 1008 | InitializeList(target); 1009 | FtdiDevice::Initialize(target); 1010 | uv_mutex_init(&libraryMutex); 1011 | } 1012 | } 1013 | 1014 | NODE_MODULE(ftdi, init) 1015 | -------------------------------------------------------------------------------- /src/ftdi_device.h: -------------------------------------------------------------------------------- 1 | #ifndef FTDI_DEVICE_H 2 | #define FTDI_DEVICE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "nan.h" 10 | 11 | using namespace v8; 12 | using namespace node; 13 | 14 | namespace ftdi_device 15 | { 16 | 17 | typedef struct 18 | { 19 | uint8_t* data; 20 | DWORD length; 21 | } ReadBaton_t; 22 | 23 | typedef struct 24 | { 25 | uint8_t* data; 26 | DWORD length; 27 | } WriteBaton_t; 28 | 29 | typedef enum 30 | { 31 | ConnectType_ByIndex, 32 | ConnectType_BySerial, 33 | ConnectType_ByDescription, 34 | ConnectType_ByLocationId, 35 | } ConnectType_t; 36 | 37 | typedef struct ConnectionParams_t 38 | { 39 | ConnectType_t connectType; 40 | char *connectString; 41 | int32_t connectId; 42 | int pid; 43 | int vid; 44 | 45 | ConnectionParams_t() 46 | { 47 | connectString = NULL; 48 | } 49 | 50 | } ConnectionParams_t; 51 | 52 | typedef struct 53 | { 54 | int baudRate; 55 | UCHAR wordLength; 56 | UCHAR stopBits; 57 | UCHAR parity; 58 | USHORT fc; 59 | UCHAR bitMode; 60 | UCHAR bitMask; 61 | bool hasBitSettings; 62 | } DeviceParams_t; 63 | 64 | typedef enum 65 | { 66 | DeviceState_Idle, 67 | DeviceState_Open, 68 | DeviceState_Closing 69 | } DeviceState_t; 70 | 71 | class FtdiDevice : public Nan::ObjectWrap 72 | { 73 | public: 74 | static void Initialize(Handle target); 75 | 76 | FtdiDevice(); 77 | ~FtdiDevice(); 78 | 79 | static NAN_METHOD(New); 80 | static NAN_METHOD(Open); 81 | static NAN_METHOD(Write); 82 | static NAN_METHOD(Close); 83 | 84 | static FT_STATUS OpenAsync(FtdiDevice* device, Nan::Callback *callback_read); 85 | static FT_STATUS ReadDataAsync(FtdiDevice* device, ReadBaton_t* baton); 86 | static FT_STATUS WriteAsync(FtdiDevice* device, WriteBaton_t* baton); 87 | static FT_STATUS CloseAsync(FtdiDevice* device); 88 | 89 | void ExtractDeviceSettings(Local options); 90 | FT_STATUS SetDeviceSettings(); 91 | FT_STATUS OpenDevice(); 92 | 93 | FT_STATUS PrepareAsyncRead(); 94 | void WaitForReadOrCloseEvent(); 95 | void SignalCloseEvent(); 96 | 97 | FT_HANDLE ftHandle; 98 | DeviceParams_t deviceParams; 99 | ConnectionParams_t connectParams; 100 | 101 | DeviceState_t deviceState; 102 | 103 | #ifndef WIN32 104 | EVENT_HANDLE dataEventHandle; 105 | #else 106 | HANDLE dataEventHandle; 107 | #endif 108 | uv_mutex_t closeMutex; 109 | }; 110 | 111 | } 112 | 113 | inline void AsyncExecuteCompletePersistent (uv_work_t* req) { 114 | Nan::AsyncWorker* worker = static_cast(req->data); 115 | worker->WorkComplete(); 116 | } 117 | 118 | inline void AsyncQueueWorkerPersistent (Nan::AsyncWorker* worker) { 119 | uv_queue_work( 120 | uv_default_loop() 121 | , &worker->request 122 | , Nan::AsyncExecute 123 | , (uv_after_work_cb)AsyncExecuteCompletePersistent 124 | ); 125 | } 126 | #endif 127 | -------------------------------------------------------------------------------- /src/ftdi_driver.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "ftdi_driver.h" 7 | #include "ftdi_constants.h" 8 | 9 | using namespace std; 10 | using namespace v8; 11 | using namespace node; 12 | 13 | 14 | /********************************** 15 | * Local typedefs 16 | **********************************/ 17 | struct DeviceListBaton 18 | { 19 | FT_DEVICE_LIST_INFO_NODE *devInfo; 20 | DWORD listLength; 21 | FT_STATUS status; 22 | int vid; 23 | int pid; 24 | 25 | DeviceListBaton() 26 | { 27 | devInfo = NULL; 28 | } 29 | }; 30 | 31 | 32 | /********************************** 33 | * Local Helper Functions protoypes 34 | **********************************/ 35 | bool DeviceMatchesFilterCriteria(FT_DEVICE_LIST_INFO_NODE *devInfo, int filterVid, int filterPid); 36 | 37 | 38 | 39 | /********************************** 40 | * Local Variables 41 | **********************************/ 42 | uv_mutex_t libraryMutex; 43 | uv_mutex_t vidPidMutex; 44 | 45 | 46 | #define JS_CLASS_NAME "FtdiDriver" 47 | 48 | 49 | /********************************** 50 | * Functions 51 | **********************************/ 52 | bool DeviceMatchesFilterCriteria(FT_DEVICE_LIST_INFO_NODE *devInfo, int filterVid, int filterPid) 53 | { 54 | int devVid = (devInfo->ID >> 16) & (0xFFFF); 55 | int devPid = (devInfo->ID) & (0xFFFF); 56 | 57 | if(filterVid == 0 && filterPid == 0) 58 | { 59 | return true; 60 | } 61 | if(filterVid != 0 && filterVid != devVid) 62 | { 63 | return false; 64 | } 65 | if(filterPid != 0 && filterPid != devPid) 66 | { 67 | return false; 68 | } 69 | return true; 70 | } 71 | 72 | DeviceListBaton* FindAllAsync(int vid, int pid) 73 | { 74 | DeviceListBaton* listBaton = new DeviceListBaton(); 75 | FT_STATUS ftStatus; 76 | DWORD numDevs = 0; 77 | 78 | // Lock Readout 79 | uv_mutex_lock(&vidPidMutex); 80 | 81 | #ifndef WIN32 82 | if(vid != 0 && pid != 0) 83 | { 84 | uv_mutex_lock(&libraryMutex); 85 | ftStatus = FT_SetVIDPID(vid, pid); 86 | uv_mutex_unlock(&libraryMutex); 87 | } 88 | #endif 89 | // create the device information list 90 | uv_mutex_lock(&libraryMutex); 91 | ftStatus = FT_CreateDeviceInfoList(&numDevs); 92 | uv_mutex_unlock(&libraryMutex); 93 | if (ftStatus == FT_OK) 94 | { 95 | if (numDevs > 0) 96 | { 97 | // allocate storage for list based on numDevs 98 | listBaton->devInfo = new FT_DEVICE_LIST_INFO_NODE[numDevs]; 99 | memset(listBaton->devInfo, 0, sizeof(FT_DEVICE_LIST_INFO_NODE) * numDevs); 100 | 101 | // get the device information list 102 | uv_mutex_lock(&libraryMutex); 103 | ftStatus = FT_GetDeviceInfoList(listBaton->devInfo, &numDevs); 104 | uv_mutex_unlock(&libraryMutex); 105 | 106 | // fallback for wrong info in several cases... when connected multiple devices and unplug one... 107 | for(DWORD i = 0; i < numDevs; i++) 108 | { 109 | uv_mutex_lock(&libraryMutex); 110 | FT_ListDevices((PVOID)i, listBaton->devInfo[i].SerialNumber, FT_LIST_BY_INDEX | FT_OPEN_BY_SERIAL_NUMBER); 111 | FT_ListDevices((PVOID)i, listBaton->devInfo[i].Description, FT_LIST_BY_INDEX | FT_OPEN_BY_DESCRIPTION); 112 | FT_ListDevices((PVOID)i, &listBaton->devInfo[i].LocId, FT_LIST_BY_INDEX | FT_OPEN_BY_LOCATION); 113 | uv_mutex_unlock(&libraryMutex); 114 | } 115 | } 116 | } 117 | uv_mutex_unlock(&vidPidMutex); 118 | 119 | listBaton->listLength = numDevs; 120 | listBaton->status = ftStatus; 121 | return listBaton; 122 | } 123 | 124 | class FindAllWorker : public Nan::AsyncWorker { 125 | public: 126 | FindAllWorker(Nan::Callback *callback, int vid, int pid) 127 | : Nan::AsyncWorker(callback), vid(vid), pid(pid) {} 128 | ~FindAllWorker() {} 129 | 130 | // Executed inside the worker-thread. 131 | // It is not safe to access V8, or V8 data structures 132 | // here, so everything we need for input and output 133 | // should go on `this`. 134 | void Execute () { 135 | listBaton = FindAllAsync(vid, pid); 136 | } 137 | 138 | // Executed when the async work is complete 139 | // this function will be run inside the main event loop 140 | // so it is safe to use V8 again 141 | void HandleOKCallback () { 142 | Nan::HandleScope scope; 143 | 144 | v8::Local argv[2]; 145 | if(listBaton->status == FT_OK) 146 | { 147 | // Determine the length of the resulting list 148 | int resultListLength = 0; 149 | for (DWORD i = 0; i < listBaton->listLength; i++) 150 | { 151 | if(DeviceMatchesFilterCriteria(&listBaton->devInfo[i], vid, pid)) 152 | { 153 | resultListLength++; 154 | } 155 | } 156 | 157 | // Create Java Script Array for the resulting devices 158 | Local array= Nan::New(resultListLength); 159 | 160 | int index = 0; 161 | for (DWORD i = 0; i < listBaton->listLength; i++) 162 | { 163 | // Add device to the array in case it matches the criteria 164 | if(DeviceMatchesFilterCriteria(&listBaton->devInfo[i], vid, pid)) 165 | { 166 | Local obj = Nan::New(); 167 | obj->Set(Nan::New(DEVICE_DESCRIPTION_TAG).ToLocalChecked(), Nan::New(listBaton->devInfo[i].Description).ToLocalChecked()); 168 | obj->Set(Nan::New(DEVICE_SERIAL_NR_TAG).ToLocalChecked(), Nan::New(listBaton->devInfo[i].SerialNumber).ToLocalChecked()); 169 | obj->Set(Nan::New(DEVICE_LOCATION_ID_TAG).ToLocalChecked(), Nan::New(listBaton->devInfo[i].LocId)); 170 | obj->Set(Nan::New(DEVICE_INDEX_TAG).ToLocalChecked(), Nan::New(i)); 171 | obj->Set(Nan::New(DEVICE_VENDOR_ID_TAG).ToLocalChecked(), Nan::New( (listBaton->devInfo[i].ID >> 16) & (0xFFFF))); 172 | obj->Set(Nan::New(DEVICE_PRODUCT_ID_TAG).ToLocalChecked(), Nan::New( (listBaton->devInfo[i].ID) & (0xFFFF))); 173 | array->Set(index++, obj); 174 | } 175 | } 176 | 177 | argv[0] = Nan::Undefined(); 178 | argv[1] = array; 179 | } 180 | // something went wrong, return the error string 181 | else 182 | { 183 | argv[0] = Nan::New(GetStatusString(listBaton->status)).ToLocalChecked(); 184 | argv[1] = Nan::Undefined(); 185 | } 186 | 187 | callback->Call(2, argv); 188 | 189 | if(listBaton->devInfo != NULL) 190 | { 191 | delete listBaton->devInfo; 192 | } 193 | delete listBaton; 194 | }; 195 | 196 | private: 197 | int vid; 198 | int pid; 199 | DeviceListBaton* listBaton; 200 | }; 201 | 202 | NAN_METHOD(FindAll) { 203 | Nan::HandleScope scope; 204 | 205 | int vid = 0; 206 | int pid = 0; 207 | Nan::Callback *callback; 208 | 209 | if (info.Length() != 3) 210 | { 211 | return Nan::ThrowError("Wrong number of arguments"); 212 | } 213 | if (info[0]->IsNumber() && info[1]->IsNumber()) 214 | { 215 | vid = (int) info[0]->NumberValue(); 216 | pid = (int) info[1]->NumberValue(); 217 | } 218 | 219 | // callback 220 | if(!info[2]->IsFunction()) 221 | { 222 | return Nan::ThrowError("Third argument must be a function"); 223 | } 224 | 225 | callback = new Nan::Callback(info[2].As()); 226 | 227 | Nan::AsyncQueueWorker(new FindAllWorker(callback, vid, pid)); 228 | return; 229 | } 230 | 231 | void InitializeList(Handle target) 232 | { 233 | Local tpl = Nan::New(); 234 | tpl->SetClassName(Nan::New(JS_CLASS_NAME).ToLocalChecked()); 235 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 236 | 237 | tpl->Set( 238 | Nan::New("findAll").ToLocalChecked() 239 | , Nan::New(FindAll) 240 | ); 241 | 242 | target->Set(Nan::New(JS_CLASS_NAME).ToLocalChecked(), tpl->GetFunction()); 243 | 244 | uv_mutex_init(&vidPidMutex); 245 | } 246 | -------------------------------------------------------------------------------- /src/ftdi_driver.h: -------------------------------------------------------------------------------- 1 | #ifndef FTDI_DRIVER_H 2 | #define FTDI_DRIVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "nan.h" 9 | 10 | using namespace v8; 11 | using namespace node; 12 | 13 | 14 | void InitializeList(Handle target); 15 | 16 | NAN_METHOD(FindAll); 17 | NAN_METHOD(SetVidPid); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /test/bnet9107Test.js: -------------------------------------------------------------------------------- 1 | var ftdi = require('../index'); 2 | 3 | ftdi.find(0x18d9, 0x01a0, function(err, devices) { 4 | var device = new ftdi.FtdiDevice(devices[0]); 5 | 6 | device.on('error', function() { 7 | 8 | }); 9 | 10 | device.on('data', function(data) { 11 | console.log(arguments); 12 | }); 13 | 14 | device.open({ 15 | baudrate: 38400, 16 | databits: 8, 17 | stopbits: 1, 18 | parity: 'none' 19 | }, function(err) { 20 | 21 | device.write([0x03, 0x30, 0x00, 0x33], function(err) { 22 | 23 | }); 24 | 25 | }); 26 | 27 | }); -------------------------------------------------------------------------------- /test/chw4000Test.js: -------------------------------------------------------------------------------- 1 | var ftdi = require('../index'); 2 | 3 | ftdi.find(0x27f4, 0x0203, function(err, devices) { 4 | var device = new ftdi.FtdiDevice(devices[0]); 5 | // or 6 | // var device = new ftdi.FtdiPort(serialnumber, locationId); 7 | // or 8 | // var device = new ftdi.FtdiSerialPort(portName, { 9 | // baudrate: 115200, 10 | // databits: 8, 11 | // stopbits: 1, 12 | // parity: 'none' 13 | // }); 14 | 15 | device.on('error', function() { 16 | 17 | }); 18 | 19 | device.on('data', function(data) { 20 | console.log(arguments); 21 | }); 22 | 23 | device.open({ 24 | baudrate: 115200, 25 | databits: 8, 26 | stopbits: 1, 27 | parity: 'none' 28 | }, function(err) {console.log(arguments); 29 | 30 | setInterval(function() { 31 | device.write([0x04, 0x00, 0x02, 0x79, 0x40], function(err) { 32 | 33 | }); 34 | }, 500); 35 | 36 | }); 37 | 38 | }); -------------------------------------------------------------------------------- /test/fabi.js: -------------------------------------------------------------------------------- 1 | // var ftdi = require('../ftdi'); 2 | var ftdi = require('../index'); 3 | 4 | //var dataToWrite = [0x04, 0x00, 0x02, 0x79, 0x40]; 5 | var dataToWrite = [0x03, 0x30, 0x00, 0x33]; 6 | var connectionSettings = 7 | { 8 | baudrate: 38400, 9 | databits: 8, 10 | stopbits: 1, 11 | parity: 'none' 12 | } 13 | 14 | // ftdi.setVidPid(0x18d9, 0x01a0); 15 | 16 | 17 | // ftdi.find(function(status, devices) {console.log("1");console.log(devices)}); 18 | 19 | // ftdi.find(0x1, function(status, devices) {console.log("2");console.log(devices)}); 20 | 21 | // ftdi.find(0x1, 0x2, function(status, devices) {console.log("3");console.log(devices)}); 22 | 23 | // ftdi.find(0x18d9, function(status, devices) {console.log("4");console.log(devices)}); 24 | 25 | // ftdi.find(0x18d9, 0x01a0, function(status, devices) {console.log("5");console.log(devices)}); 26 | 27 | 28 | ftdi.find(0x18d9, 0x01a0, function(status, devices) 29 | { 30 | if(devices.length > 0) 31 | { 32 | var device = new ftdi.FtdiDevice(devices[0]); 33 | 34 | // setInterval(loop(device)) 35 | loop(device); 36 | 37 | } 38 | else 39 | { 40 | console.log("No Device found"); 41 | } 42 | }); 43 | 44 | var loop = function(device) 45 | { 46 | device.on('error', function(error) 47 | { 48 | console.log("Error: " + error) 49 | }); 50 | 51 | device.on('data', function(data) 52 | { 53 | console.log('Output: ', data.length); 54 | console.log( data ); 55 | 56 | device.close(function(status) 57 | { 58 | console.log("JS Close Device"); 59 | setTimeout(function() {loop(device);}, 5000); 60 | // loop(device); 61 | }); 62 | }); 63 | 64 | device.open(connectionSettings, function(status) 65 | { 66 | device.write(dataToWrite); 67 | }); 68 | } 69 | 70 | 71 | 72 | 73 | 74 | // var device = ftdi.Ftdi(); 75 | // var devices = ftdi.find(0x18d9, 0x01a0, function() {}); 76 | // console.log( devices ); 77 | 78 | // device.on('data', function(data) 79 | // { 80 | // console.log('Output:'); 81 | // console.log( data ); 82 | // }); 83 | 84 | // if(devices.length > 0) 85 | // { 86 | // device.open(devices[0].serial); 87 | 88 | 89 | // setInterval(function() 90 | // { 91 | // device.write(dataToWrite); 92 | // }, 2000); 93 | // } 94 | // else 95 | // { 96 | // console.log("No Device found"); 97 | // } 98 | 99 | -------------------------------------------------------------------------------- /test/ftdi.coffee: -------------------------------------------------------------------------------- 1 | ON = 1 2 | OFF = 0 3 | 4 | async = require "async" 5 | 6 | ftdi = require "../index" 7 | 8 | device = null 9 | 10 | turnBy = (miliseconds, callback) => 11 | array = new Array(ON, ON, ON, ON).reverse() 12 | device.write [parseInt(array.join(""), 2)], (err) => 13 | setTimeout => 14 | array = new Array(OFF, OFF, OFF, OFF).reverse() 15 | device.write [parseInt(array.join(""), 2)], (err) => 16 | callback() 17 | , miliseconds 18 | 19 | async.series [ 20 | (cb) => 21 | ftdi.find (err, devices) => 22 | device = new ftdi.FtdiDevice(devices[0]) 23 | cb err 24 | ], (err) => 25 | 26 | deviceOptions = 27 | baudrate: 9600 28 | databits: 8 29 | stopbits: 1 30 | parity: 'none' 31 | bitmode: 0x04 32 | bitmask: 0xf 33 | 34 | 35 | #device.on "error", (data) => 36 | # console.log "err", data 37 | # 38 | #device.on "data", (data) => 39 | # console.log "data", data 40 | # 41 | #device.on "open", () => 42 | # console.log "open" 43 | 44 | 45 | #console.log device 46 | device.open deviceOptions, (err) => 47 | 48 | #console.log(err) 49 | turnBy 4000, () => 50 | console.log "finished turning" 51 | 52 | #device.on "data", (data) => 53 | # console.log "data", data.toString("utf8") 54 | -------------------------------------------------------------------------------- /test/ftdi_unplug.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define WAIT_TIME_MILLISECONDS 250 9 | 10 | #define NANOSECS_PER_SECOND 1000000000 11 | #define NANOSECS_PER_MILISECOND 1000000 12 | #define MILISECS_PER_SECOND 1000 13 | 14 | typedef struct 15 | { 16 | FT_HANDLE handle; 17 | int vid; 18 | int pid; 19 | char* serial; 20 | int baud; 21 | 22 | pthread_t thread; 23 | EVENT_HANDLE rxEvent; 24 | } DeviceParams_t; 25 | 26 | #ifdef CHW400 27 | DeviceParams_t deviceList[] = { 28 | { 29 | 0, 30 | 0x27f4, 31 | 0x0203, 32 | (char*)"FTVTIXBE", 33 | 115200, 34 | 0, 35 | 0, 36 | }, 37 | { 38 | 0, 39 | 0x27f4, 40 | 0x0203, 41 | (char*)"FTVTIXI5", 42 | 115200, 43 | 0, 44 | 0, 45 | }, 46 | }; 47 | #elif BNET 48 | DeviceParams_t deviceList[] = { 49 | { 50 | 0, 51 | 0x18D9, 52 | 0x01A0, 53 | (char*)"00000225", 54 | 38400, 55 | 0, 56 | 0, 57 | }, 58 | { 59 | 0, 60 | 0x18D9, 61 | 0x01A0, 62 | (char*)"00000210", 63 | 38400, 64 | 0, 65 | 0, 66 | }, 67 | { 68 | 0, 69 | 0x18D9, 70 | 0x01A0, 71 | (char*)"FTUD6GBW", 72 | 38400, 73 | 0, 74 | 0, 75 | }, 76 | }; 77 | #endif 78 | 79 | pthread_t findThread; 80 | 81 | FT_STATUS Find(int vid, int pid); 82 | FT_STATUS OpenDevice(DeviceParams_t* device); 83 | void* ReadData(void* ptr); 84 | void WaitForData(DeviceParams_t* device); 85 | 86 | 87 | FT_STATUS Find(int vid, int pid) 88 | { 89 | FT_STATUS status; 90 | FT_SetVIDPID(vid, pid); 91 | 92 | DWORD numDevs = 0; 93 | status = FT_CreateDeviceInfoList(&numDevs); 94 | 95 | if (numDevs > 0) 96 | { 97 | // allocate storage for list based on numDevs 98 | FT_DEVICE_LIST_INFO_NODE *devInfo = new FT_DEVICE_LIST_INFO_NODE[numDevs]; 99 | memset(devInfo, 0, numDevs*sizeof(FT_DEVICE_LIST_INFO_NODE)); 100 | 101 | // get the device information list 102 | status = FT_GetDeviceInfoList(devInfo, &numDevs); 103 | 104 | printf("\nFound Devices: %d\r\n", numDevs); 105 | for(int i = 0; i < numDevs; i++) 106 | { 107 | // FT_HANDLE handy; 108 | 109 | // FT_GetDeviceInfoDetail (i, 110 | // &devInfo[i].Flags, 111 | // &devInfo[i].Type, 112 | // &devInfo[i].ID, 113 | // &devInfo[i].LocId, 114 | // devInfo[i].SerialNumber, 115 | // &devInfo[i].Description, 116 | // &handy); 117 | 118 | printf("%s\r\n", devInfo[i].Description); 119 | printf("%s\r\n", devInfo[i].SerialNumber); 120 | printf("Open: %d\n", devInfo[i].Flags & 0x1); 121 | } 122 | 123 | delete[] devInfo; 124 | } 125 | return status; 126 | 127 | } 128 | 129 | FT_STATUS OpenDevice(DeviceParams_t* device) 130 | { 131 | FT_STATUS status; 132 | FT_SetVIDPID(device->vid, device->pid); 133 | status = FT_OpenEx((PVOID) device->serial, FT_OPEN_BY_SERIAL_NUMBER, &(device->handle)); 134 | 135 | if(status == FT_OK) 136 | { 137 | status = FT_SetDataCharacteristics(device->handle, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE); 138 | } 139 | 140 | if(status == FT_OK) 141 | { 142 | status = FT_SetBaudRate(device->handle, device->baud); 143 | } 144 | 145 | if(status == FT_OK) 146 | { 147 | // pthread_mutex_init(&device->rxEvent.eMutex, NULL); 148 | // pthread_cond_init(&device->rxEvent.eCondVar, NULL); 149 | 150 | // status = FT_SetEventNotification(device->handle, FT_EVENT_RXCHAR, (PVOID) &(device->rxEvent)); 151 | 152 | // pthread_create(&(device->thread), NULL, ReadData, (void*)device); 153 | } 154 | 155 | printf ("Open state [%x]\r\n", status); 156 | return status; 157 | } 158 | 159 | 160 | void* ReadData(void* ptr) 161 | { 162 | DeviceParams_t* device = (DeviceParams_t*)ptr; 163 | DWORD RxBytes; 164 | FT_STATUS status; 165 | 166 | printf("Thread Started\r\n"); 167 | 168 | while(true) 169 | { 170 | WaitForData(device); 171 | 172 | status = FT_GetQueueStatus(device->handle, &RxBytes); 173 | if(status != FT_OK) 174 | { 175 | fprintf(stderr, "Can't read from ftdi device: %d\n", status); 176 | return NULL; 177 | } 178 | 179 | //printf("RX %s [%d]\r\n", device->serial, RxBytes); 180 | if(RxBytes > 0) 181 | { 182 | DWORD BytesReceived; 183 | char* data = new char[RxBytes]; 184 | 185 | FT_Read(device->handle, data, RxBytes, &BytesReceived); 186 | 187 | fprintf(stderr, "%d Bytes Read\n", BytesReceived); 188 | delete[] data; 189 | } 190 | } 191 | 192 | return NULL; 193 | } 194 | 195 | 196 | void WaitForData(DeviceParams_t* device) 197 | { 198 | struct timespec ts; 199 | struct timeval tp; 200 | 201 | gettimeofday(&tp, NULL); 202 | 203 | int additionalSeconds = 0; 204 | int additionalMilisecs = 0; 205 | additionalSeconds = (WAIT_TIME_MILLISECONDS / MILISECS_PER_SECOND); 206 | additionalMilisecs = (WAIT_TIME_MILLISECONDS % MILISECS_PER_SECOND); 207 | 208 | long additionalNanosec = tp.tv_usec * 1000 + additionalMilisecs * NANOSECS_PER_MILISECOND; 209 | additionalSeconds += (additionalNanosec / NANOSECS_PER_SECOND); 210 | additionalNanosec = (additionalNanosec % NANOSECS_PER_SECOND); 211 | 212 | /* Convert from timeval to timespec */ 213 | ts.tv_sec = tp.tv_sec; 214 | ts.tv_nsec = additionalNanosec; 215 | ts.tv_sec += additionalSeconds; 216 | 217 | pthread_mutex_lock(&device->rxEvent.eMutex); 218 | pthread_cond_timedwait(&device->rxEvent.eCondVar, &device->rxEvent.eMutex, &ts); 219 | pthread_mutex_unlock(&device->rxEvent.eMutex); 220 | } 221 | 222 | void* FindThread(void* ptr) 223 | { 224 | while(1) 225 | { 226 | Find(deviceList[0].vid, deviceList[0].pid); 227 | sleep(1); 228 | } 229 | } 230 | 231 | 232 | int main (int argc, char **argv) 233 | { 234 | printf("Open Device 1\r\n"); 235 | OpenDevice(&deviceList[0]); 236 | sleep(3); 237 | printf("Open Device 2\r\n"); 238 | OpenDevice(&deviceList[1]); 239 | 240 | pthread_create(&findThread, NULL, FindThread, NULL); 241 | while(true) 242 | { 243 | sleep(1); 244 | } 245 | pthread_join(deviceList[0].thread, NULL); 246 | } -------------------------------------------------------------------------------- /test/multlipleDevicesTest.js: -------------------------------------------------------------------------------- 1 | var ftdi = require('../index'); 2 | 3 | var foundDevs = []; 4 | 5 | setInterval(function() { 6 | ftdi.find(0x27f4, 0x0203, function(err, devices) { 7 | foundDevs = devices; 8 | console.log(devices.length); 9 | console.log(devices); 10 | }); 11 | }, 150); 12 | 13 | setTimeout(function() { 14 | var device1 = new ftdi.FtdiDevice({ serialNumber: 'FTVTIXI5', vendorId: 0x27f4, productId: 0x0203 }); 15 | 16 | device1.on('data', function(data) { 17 | // console.log(arguments); 18 | }); 19 | 20 | device1.open({ 21 | baudrate: 115200, 22 | databits: 8, 23 | stopbits: 1, 24 | parity: 'none' 25 | }, function(err) { 26 | console.log('opened first'); 27 | // console.log('Plug new device now!!!'); 28 | // setInterval(function() { 29 | // device1.write([0x04, 0x00, 0x02, 0x79, 0x40], function(err) { 30 | 31 | // }); 32 | // }, 200); 33 | }); 34 | }, 3000); 35 | 36 | 37 | setTimeout(function() { 38 | var device2 = new ftdi.FtdiDevice({ serialNumber: 'FTVTIXBE', vendorId: 0x27f4, productId: 0x0203 }); 39 | 40 | device2.on('data', function(data) { 41 | // console.log(arguments); 42 | }); 43 | 44 | device2.open({ 45 | baudrate: 115200, 46 | databits: 8, 47 | stopbits: 1, 48 | parity: 'none' 49 | }, function(err) { 50 | console.log('opened second'); 51 | // setInterval(function() { 52 | // device2.write([0x04, 0x00, 0x02, 0x79, 0x40], function(err) { 53 | 54 | // }); 55 | // }, 200); 56 | }); 57 | }, 10000); 58 | 59 | -------------------------------------------------------------------------------- /test/openBySerialTest.js: -------------------------------------------------------------------------------- 1 | setTimeout(function() { 2 | var ftdi = require('../index'); 3 | 4 | var chw4000 = new ftdi.FtdiDevice({ serialNumber: 'FTVTIXBE' }); 5 | //var chw4000 = new ftdi.FtdiDevice({ locationId: 530 }); 6 | 7 | chw4000.on('data', function(data) { 8 | console.log(arguments); 9 | }); 10 | 11 | chw4000.open({ 12 | baudrate: 115200, 13 | databits: 8, 14 | stopbits: 1, 15 | parity: 'none' 16 | }, function(err) {console.log(arguments); 17 | 18 | setInterval(function() { 19 | chw4000.write([0x04, 0x00, 0x02, 0x79, 0x40], function(err) {console.log(arguments); 20 | 21 | }); 22 | }, 200); // 200 for serialNumber 23 | 24 | }); 25 | 26 | 27 | setTimeout(function() { 28 | 29 | var bnet9107 = new ftdi.FtdiDevice({ serialNumber: '00000210' }); 30 | //var bnet9107 = new ftdi.FtdiDevice({ locationId: 529 }); 31 | bnet9107.on('data', function(data) { 32 | console.log(arguments); 33 | }); 34 | 35 | bnet9107.open({ 36 | baudrate: 38400, 37 | databits: 8, 38 | stopbits: 1, 39 | parity: 'none' 40 | }, function(err) {console.log(arguments); 41 | 42 | setInterval(function() { 43 | bnet9107.write([0x03, 0x30, 0x00, 0x33], function(err) {console.log(arguments); 44 | 45 | }); 46 | }, 300); 47 | 48 | }); 49 | 50 | }, 2000); 51 | }, 300); 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /test/pollerTest.js: -------------------------------------------------------------------------------- 1 | var ftdi = require('../index'); 2 | 3 | setInterval(function() { 4 | console.log(new Date(), ' "CHW-4000": -- find STARTED -- '); 5 | ftdi.find(0x27f4, 0x0203, function(err, devices) { 6 | console.log(new Date(), ' "CHW-4000": -- find FINISHED -- '); 7 | console.log(arguments); 8 | }); 9 | }, 1000); 10 | 11 | setInterval(function() { 12 | console.log(new Date(), ' "BNET9107": -- find STARTED -- '); 13 | ftdi.find(0x18d9, 0x01a0, function(err, devices) { 14 | console.log(new Date(), ' "BNET9107": -- find FINISHED -- '); 15 | console.log(arguments); 16 | }); 17 | }, 1000); -------------------------------------------------------------------------------- /test/pollerTest2.js: -------------------------------------------------------------------------------- 1 | var ftdi = require('../index'); 2 | 3 | var chw4000Connected = false, 4 | bnet9107Connected = false; 5 | 6 | var isChw4000Polling = false, 7 | isBnet9107Polling = false; 8 | 9 | setInterval(function() { 10 | if (isChw4000Polling) return; 11 | console.log('\n--------------'); 12 | console.log(new Date(), ' "CHW-4000": -- find STARTED -- '); 13 | isChw4000Polling = true; 14 | ftdi.find(0x27f4, 0x0203, function(err, devices) { 15 | isChw4000Polling = false; 16 | console.log(new Date(), ' "CHW-4000": -- find FINISHED -- '); 17 | console.log(arguments); 18 | 19 | if (!chw4000Connected && devices.length > 0) { 20 | var device = new ftdi.FtdiDevice(devices[0]); 21 | chw4000Connected = true; 22 | 23 | device.on('error', function() { 24 | }); 25 | device.on('data', function(data) { 26 | console.log(arguments); 27 | }); 28 | 29 | console.log(new Date(), ' "CHW-4000": -- open STARTED -- '); 30 | device.open({ 31 | baudrate: 115200, 32 | databits: 8, 33 | stopbits: 1, 34 | parity: 'none' 35 | }, function(err) { 36 | console.log(new Date(), ' "CHW-4000": -- open FINISHED -- '); 37 | 38 | console.log(new Date(), ' "CHW-4000": -- write STARTED -- '); 39 | device.write([0x04, 0x00, 0x02, 0x79, 0x40], function(err) { 40 | console.log(new Date(), ' "CHW-4000": -- write FINISHED -- '); 41 | }); 42 | }); 43 | } 44 | }); 45 | }, 5000); 46 | 47 | setInterval(function() { 48 | if (isBnet9107Polling) return; 49 | console.log('\n--------------'); 50 | console.log(new Date(), ' "BNET9107": -- find STARTED -- '); 51 | isBnet9107Polling = true; 52 | ftdi.find(0x18d9, 0x01a0, function(err, devices) { 53 | isBnet9107Polling = false; 54 | console.log(new Date(), ' "BNET9107": -- find FINISHED -- '); 55 | console.log(arguments); 56 | 57 | if (!bnet9107Connected && devices.length > 0) { 58 | var device = new ftdi.FtdiDevice(devices[0]); 59 | bnet9107Connected = true; 60 | 61 | device.on('error', function() { 62 | }); 63 | device.on('data', function(data) { 64 | console.log(arguments); 65 | }); 66 | 67 | console.log(new Date(), ' "BNET9107": -- open STARTED -- '); 68 | device.open({ 69 | baudrate: 38400, 70 | databits: 8, 71 | stopbits: 1, 72 | parity: 'none' 73 | }, function(err) { 74 | console.log(new Date(), ' "BNET9107": -- open FINISHED -- '); 75 | 76 | console.log(new Date(), ' "BNET9107": -- write STARTED -- '); 77 | device.write([0x03, 0x30, 0x00, 0x33], function(err) { 78 | console.log(new Date(), ' "BNET9107": -- write FINISHED -- '); 79 | }); 80 | }); 81 | } 82 | }); 83 | }, 5000); --------------------------------------------------------------------------------