Tagged: pick and place Toggle Comment Threads | Keyboard Shortcuts

  • charliex 11:35 pm on January 31, 2019 Permalink | Reply
    Tags: neoden, neoden 4, pick and place   

    Neoden 4 Pick and Place 


    This is a long post.

    We replaced out Neoden TM220A with a Neoden 4, it has worked well for us but there was a couple of things I wanted to change in the software. Like the machine moves over the pulled tape with parts and sometimes the tape gets in the way. maybe some changes to the vision algorithms.

    So where to begin.

    The easy one; if you use Eagle and want a slightly better experience https://github.com/charlie-x/neoden4 use my ULP, if you don’t use Eagle the various formats are documented inside that ULP

    The Neoden4 is a Windows XP box that is locked down a little bit, ctrl alt delete gets you to the Chinese task manager, select the  the lower middle button, then select the run menu and run CMD or explorer.


    It has a C: Write cache protection, anything you write to the C: drive goes into a cache file that is located on the root of drive C: so if you mod the registry or such, and reboot the changes will be gone.

    To get rid of it, boot XP in safe mode, then run



    It’ll look better on the machine. click the lower right button down and this will remove the KYSysProtect.sys driver, using the first button will re-enable it.

    [INSTALL]                     [CANCEL]





    Then you can pop in the registry editor regedit.exe . Change shell from start.bat to explorer.exe

    Reboot and you should see explorer, the D:\Neoden4\ folder contains the startup executable. Now you can add a usb to wifi or wired adapter and setup networking.

    Either of these will work  and have XP drivers




    The run csv files are stored in D:\Neoden\proc folder

    The footprints are stored inside the config folder as pkg.csv







    config.ini contains all the preset positions of the feeders, etc they’re often in QT variant format but QT supports either



















    Either is valid

    Setting.ini has the language and hashed serial number of your machines MAC address if this doesn’t match it’ll default to chinese





    If you don’t turn off ksysprotect the only changes you make that will stay after a reboot will be on the D:\ drive.

    The update installed is a .cab file renamed to .neoden and the start.bat script looks for it on the USB drive and will extract it into the D:\Neoden4 folder at boot time.

    @echo off

    if not exist D:/NeoDen4/v4.1.3.9_1d6d860d134a44232ea1b42c0bc11e52.neoden goto run

    expand D:/NeoDen4/v4.1.3.9_1d6d860d134a44232ea1b42c0bc11e52.neoden -F:* ./

    del v4.1.3.9_1d6d860d134a44232ea1b42c0bc11e52.neoden


    start NeoDen4.exe


    The shutdown button in the neoden software just shells out to the cmd processor and the shutdown command, this is pretty typical for these sorts of machines.

    If you switch the XP language to English, it will likely no longer boot the needed files are missing even if you add the language pack, better to reinstall.

    The ID for the Chinese/English switch is based on the first network MAC address the machine finds. If it appears in Chinese and was English, then either the config file was changed or the first MAC address did

    Drivers for the cameras are on my github for 7×64 and 10 x64 , probably 8 too , Be aware since after years of people beating on Windows for the fault of bad drivers MS has stepped up the driver policy on 10 and you need to generate and resign the CAB as well the INF otherwise you’ll have to disable secure boot and/or driver enforcement to load modified drivers.

    Better translations

    On the whole the translations are fine, but there were some things that bugged me and used up valuable screen space. Like the Feeder column Lists Feeder 1, Feeder 2 etc, leaving less space for what is in the column that you really want to see. So just changing it in the QM file, the app uses QT 5.3.1 extract it to a C:\QM folder

    The translations are stored in the Neoden.qm file which is a compiled XML/TS translation file.

    QT Linguist can open QM files, you just need to change the filter in the open, but i used the command line tool instead.

    C:\Qt\QT5.3.1\5.3\mingw482_32\bin\lconvert.exe Neoden4.qm -o Neoden4.ts

    This will convert the binary .qm to an XML file. If you open it up you’ll see the translations like th

            <source>55å·æ–™æ ˆ</source>
           <translation>Feeder 55</translation>


    <message>    <source>å☼-åo^</source>

    < /message>

    The source is the Chinese, which doesn’t show up here and translation is the English version. So next changed all the relevant Feeder messages to > </ as if it were removed altogether and didn’t leave a single space the Chinese translation would appear instead



            <source>æ-Tæ ^</source>
            <translation> </translation>

    < /message>

    To convert the XML back to a .QM use the command

    C:\Qt\QT5.3.1\5.3\mingw482_32\bin\lrelease.exe Neoden4.ts

    The QM likely changes between versions, so best to always grab the current one.



    After, you can see the space before the numbers and you can stretch out the SMD Spec column


    Fixing the MAC address

    If you want to keep the same setup but either change the current mini PC< run it on another system or add a network card that messes up the install, you can either change the mac address so its the same as the original, or you can hard code the MAC address in QT this does mean setting up QT to compile with mingw etc.

    If you haven’t grabbed QT 5.31 do so and extract it to C:\QT\QT5.3.1

    The software doesn’t use any of the networking except for the MAC address

    Open the file


    Look for

    QString QNetworkInterface::hardwareAddress() const

    In the following code substitute the desired MAC address for 00:00:00:00:00:00.

            Returns the low-level hardware address for this interface. On
           Ethernet interfaces, this will be a MAC address in string
           representation, separated by colons.
           Other interface types may have other types of hardware
            addresses. Implementations should not depend on this function
            returning a valid MAC address.


    QString QNetworkInterface::hardwareAddress() const

           return QString(“00:00:00:00:00:00”);
           return d ? d->hardwareAddress : QString();


    Now recompile QT

    configure.bat -release -qt-zlib -opensource -confirm-license -platform win32-g++ -opengl desktop -prefix C:\Qt\Qt5.3.1\5.3\mingw48_32 -nomake tests -nomake examples

    Then copy over the new Qt5Network.dll to the machine or your local install and test it.

    Change the Logo

    This is a simple one, change Logo.png in the D:\Neoden4\Res folder to a PNG of your choice that is 500×100 32 bit with an alpha background




    If for some reason you don’t want the machine to shut down after you click Shut Down search and replace shutdown in the Neoden4.exe with an invalid cmd. Like xxxxxx then it won’t execute it. It is in a couple of spots. Entering the config mode incorrectly also tells the machine to shutdown, which is annoying if you do run it on a different PC



    Running on a different PC

    You’ll need the MAC address fix from above otherwise it’ll be in Chinese. Copy over all the files from D:\neoden4\ to the new machine , doesn’t really matter where. But you will need a serial port that is at COM2:  and the same drive letter as the USB drive is now.

    If you run XP the drivers for the cameras are in the D:\Neoden4 folder, if you want x64 or newer windows we will have to make some up, which just means grabbing the right version of the Cypress FX usb drivers and adding the Cameras USB ID’s

    As a side note you can use software like KernelPro to forward USB and the Serial over TCP/IP to any other machine on the network and it’ll work fine as long as you install the camera drivers and the forwarded Serial is COM2

    com0com should work too, but you will need to forward the USB as well, i’ve used the Kernel Pro software a fair but and its been pretty solid and useful for sniffing, RE etc.


    The Windows DDK also has the skeleton code for a Virtual Serial Port I’ve used that one extensively too

    Another great tool for sniffing is Device Monitoring Studio, came in handy when  building the keyboard.

    This of course needs a network adapter on the Neoden4 side in order to forward the Serial/USB too

    The Cameras

    Camera USB ID





    As we can clearly see its a Cypress FX with CYUSB3.sys driver. The machine had a folder called CameraNew that had the XP32 drivers, but we may want to update those.

    Just need to add this to the cyusb3.inf driver. (which i’ve linked here )

    VID_52CB&PID_52CB.DeviceDesc=”Camera Neoden Tech.”

    and to each relevant section (the platforms)

    %VID_52CB&PID_52CB.DeviceDesc%=CyUsb3, USB\VID_52CB&PID_52CB

    e.g. add it to each of these sections

    ;for all platforms


    ;for x86 platforms


    Add the same line to each of these Device sections for the OS you want to create the driver , all of them for completeness

    Cypress actually document the process here, as well as locating the drivers you might need, i used windows 7 x64 as my base


    To match the device with the drivers, refer to the steps mentioned under the section “Matching Devices to the Driver ” in the attached PDF file. Adding the VID/PID is already done in the attached .inf file , so you can skip “Step A : Add the device’s Vendor ID and Product ID to the CYUSB3.INF file”.

    Now you will have camera drivers for your system and can install them when you add the USB cameras.

    The serial connection is hardwired on the motherboard of the mini PC which is inside the PNP, in this post I am mostly going to concentrate on the software side and to be honest if you can’t find the bits to rewire in the new PC you should probably leave it alone as it does have a few things going on. But you do just need to reroute USB (cameras) and Serial(Control) . So they’re fairly straightforward.

    USB to serial is fine. since i have ran it plenty of times with a serial<>tcpip bridge

    You can also use these drivers for a fresh XP install that uses the language set of your choice.

    As long as you copy all the opencv dlls, and install folder on D: from the machine  that is all you need to make a fresh install on the same PC, if you change the PC you will need to do MAC fix or it might default to the Chinese language, which is fine if you speak Chinese.

    I didn’t really look into how they generate the config password since its so easy to replicate a MAC , but that is what it is based on, i may take a look sometime.

    Other Mods to the GUI

    I wanted rid of the frameless app so it can be resized on a larger monitor, since the app uses QT it’ll use setWindowsFlags with


    Which is 0x0000800

    Looking thru the binary we can see the calls to setWindowsFlags , it is mangled so using a demangle tool we can see this is correct


    Which is right, and there is the flag being placed on the stack for the call, so just change it to 0 and  it will now run with a resizeable border

    C7 04 24 00 08 00 00   mov     dword ptr [esp], 0x800

    FF 15 EC 04 4F 00       call  ds:_ZN7QWidget14setWindowFlagsE6QFlagsIN2Qt10WindowTypeEE

    the qt_main is where the main window is created, which itself is called from WinMain, so traverse WinMain to  the QT main routine looking for

    QApplication::QApplication(int&, char**, int)

    which in this case is mangled as


    Although I usually find QT apps a PITA to RE they do all share common flows so its easy to find the basics

    We’ll know where the main window is created in that function since it’ll have



    But we have enough info with the C7 04 24 00 08 00 00  FF 15 EC, just search and replace that with C7 04 24 00 00 00 00 FF 15 EC a global replace does change all the windows to be bordered, but you might not want to change all of them, next i want to change all the tabs to automatically expand, but i haven’t done that yet, and Neoden do actually keep doing updates.



    Most of the changes they’re making seem mostly related to text fixes ,maybe since they did a Neoden USA partnership


    Then it can run remotely on Windows 7 x64 etc with just the pnp computer being the remote host.


    Gaining control

    Lets cover getting access to the control systems if we want to change the software altogether

    It is all serial to the motion controller board so it is trivial to snoop, after that there is CAN bus but it doesn’t really feel like a necessity to go that far yet, (apparentlyit now is ) maybe if people wanted to use the feeders or peelers for other projects but there are better ones. Also i wonder if its any coincidence that serial byte size is 8 bytes, which is what you send in the most common CAN frame.

    Here is the boot up trace, these are annotated for the test software with a C++ wrapper for the RS232dll.dll so it can snoop, change or inject if needed

    /// boot to main menu

    //status msg

    Write 0x45Read 0x09

    Write 0x05

    Read 0x14

    Write 0x85

    Read 0x1c

    Read 0x00 0x03 0x00 0x16 0x00 0x00 0x00 0x00 0x5d

    // blow/suck
        Write 0x43

    Read 0x0f

    Write 0xc3

    Read 0x07

    // blow nozzle 1

    Write 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0xb2

    Write 0x03
        Read 0x03

    Write 0x03

    Read 0x47

    Write 0x43
        Read 0x0f

    Write 0xc3

    Read 0x07

    // blow nozzle 2

    Write 0x01 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0x30

    Write 0x03
        Read 0x03

    Write 0x03

    Read 0x47

    Write 0x43
        Read 0x0f

    Write 0xc3

    Read 0x07

    // blow nozzle 3

    Write 0x01 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x51

    Write 0x03
        Read 0x03

    Write 0x03

    Read 0x47

    Write 0x43
        Read 0x0f

    Write 0xc3

    Read 0x07

    // blow nozzle 4

    Write 0x01 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x15

    Write 0x03

    Read 0x03

    Write 0x03

    Read 0x47

    Write 0x43
        Read 0x0f

    Write 0xc3

    Read 0x07

    // off nozzle 1

    Write 0x00 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x61

    Write 0x03

    Read 0x03

    Write 0x03

    Read 0x47

    Write 0x43
        Read 0x0f

    Write 0xc3

    Read 0x07

    // off nozzle 2

    Write 0x00 0x02 0x00 0x00 0x00 0x00 0x00 0x00 0xe3

    Write 0x03

    Read 0x03

    Write 0x03

    Read 0x47

    Write 0x43
        Read 0x0f

    Write 0xc3

    Read 0x07

    // off nozzle 3

    Write 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x82

    Write 0x03

    Read 0x03

    Write 0x03

    Read 0x47

    Write 0x43
        Read 0x0f

    Write 0xc3

    Read 0x07

    // off nozzle 4

    Write 0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0xc6

    Write 0x03

    Read 0x03

    Write 0x03

    Read 0x47

    //blow all off
        Write 0x43

    Read 0x0f

    Write 0xc3

    Read 0x07

    // all off?

    Write 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

    Write 0x03

    Read 0x47

    //rotate nozzles
        Write 0x41

    Read 0x0d

    Write 0xc1

    Read 0x05

    Write 0x00 0x00 0x96 0x00 0x00 0x00 0x00 0x00 0x45

    Write 0x01

    Read 0x01

    Write 0x01

    Read 0x45

    // move nozzles, max retract
        Write 0x42

    Read 0x0e

    Write 0xc2

    Read 0x06

    Write 0x00 0x00 0x96 0x00 0x00 0x00 0x00 0x00 0x45

    Write 0x02

    Read 0x11

    // rails set speed to 00
        Write 0x46

    Read 0x0a

    Write 0xc6

    Read 0x02

    Write 0x64 0x09 0x00 0x00 0x00 0x00 0x00 0x00 0x19

    Write 0x06

    Read 0x06

    Write 0x06

    Read 0x42

    //rail set speed to 100%

    Write 0x46

    Read 0x0a

    Write 0xc6

    Read 0x02

    Write 0x64 0x09 0x00 0xc8 0x00 0x00 0x00 0x00 0x8c

    Write 0x06

    Read 0x06

    Write 0x06

    Calculating the CRC Routines

    So looking at the data, its a cmd, reply, cmd reply, cmd with extra data and an obvious CRC at the end. Going back to the binary lets see what CRC tables there are.

    Yep sure enough there are two common CRCs

    A full CRC16 table for poly 0x1021

    dw 0, 4129, 8258, 12387, 16516, 20645, 24774, 28903, 33032

                 37161, 41290, 45419, 49548, 53677, 57806, 61935, 4657

    Second table is the first 16 words of the same table

    dw 0, 4129, 8258, 12387, 16516, 20645, 24774, 28903, 33032, 161, 41290, 45419, 49548, 53677, 57806, 61935

    Looking for references to this we see a normal CRC16, its next to com port write/write (which we can tell from the references to the rs232dll.dll )

    For the smaller table its a different routine. Digging into it a bit we see it is taking the cmd we sent, the reply from the machine , masking and then calculating the crc from the smaller table.

    push    ebx

    sub     esp, 0x28

    mov     ebx, [esp+0x2C+arg_4]

    mov     eax, [esp+0x2C+arg_0]

    mov     [esp+2Ch+var_28], 2

    mov     [esp+2Ch+var_E], al

    mov     eax, ebx

    and     ebx, 0Fh

    and     eax, 0FFFFFFF0h

    mov     [esp+0x2C+var_D], al

    lea     eax, [esp+-0x2C+var_E]

    mov     [esp+2Ch+var_2C], eax

    call    crc16_16

    and     eax, 0x0F

    cmp     al, bl

    setz    al

    add     esp, 0x28

    pop     ebx

    retn    8

    So it is pulling in cmd( sent by software) and the reply from the machine, combining them into a 16 bit value ,masking it off to be the top nybble, then crcing those two bytes, then it masks the lower nybble of the calculated CRC and the reply from the machine then check to see if they match, converting it to C

    bool  checkReply ( char cmd, char reply )

           // combine and mask
           int16_t v3 = ( cmd + ( int16_t ( reply  << 8 ) ) ) & 0xf0ff;
           // technically it calls the full 32bit crc, but we only need the lower nybble
            uint16_t crc = crc16_16 ( ( uint8_t * ) &v3, 2 );
           // mask off lower nybble
            crc &= 0x0f;
           reply &= 0x0f;
           // if they match, we’re good.
            return ( crc == reply  );


    Lets test it out, i also put the CRC routine here

    Launch C++

    Feeding it a couple of cmds that the machine says are valid, and one bad command we get

    CMD = 0xC3 reply = 0x07 = yes

    CMD = 0x45 reply = 0x09 = yes

    CMD = 0x45 reply = 0xAA = no

    Great we know know that this is how the machine replies to commands.

    Next is the CRC at the end of the larger blocks, this one was fixed at 2 bytes, so we know it is not it. We know there is a second CRC16 table, so looking at the references for that we find the standard CRC16 routine, and then referring to that we see the routine that then spits out the 9 bytes. The RS232 dll has a separate routine for sending out the single bytes.

    Just to make it easy to prove out we can use an online tool to verify it is what I think it is.

    Online CRC Calculator

    Lets take a couple of known strings

    0x64 0x09 0x00 0x00 0x00 0x00 0x00 0x00  CRC=0x19

    0x64 0x09 0x00 0xc8 0x00 0x00 0x00 0x00  CRC=0x8C

    Set CRC to HEX and 16

    Result 1


    Result 2


    There we go, CRC16/XMODEM i know its polynomial 0x1021 and i know the seed is 0 and Neoden only uses the lower byte

    So now we know what both the CRC routines are, how the basic command structure works and we can go from there

    The next part is a little tedious since it means we snoop the serial, and select one command at a time, see what happens and try variations on it, record, test and repeat.. basic science . Luckily there aren’t that many.

    // a few hours pass by

    A quick C++ class to define a basic test harness with each part added as more information is discovered, creating a class for sub components like the Rails, feeders etc and then an embodied class for the machine.

    Taking just one simple example, the buzzer

    // beep if true, off if false
                 // @return pass/fail
                bool Buzzer ( bool on ) {
                    if ( bBoot == false ) { return false; }
                    if ( getAck ( 0x47 ) == false ) {
                         return false;
                    if ( getAck ( 0xc7 ) == false ) {
                          return false;
                    uint8_t cmd[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
                    if ( on ) {
                          cmd[5] = 0x01;
                    //doesn’t expect a reply from the machine
                     if ( sendData ( cmd, 8 ) != 8 ) {
                        return false;
                    return getAck ( 0x07 );

    The flow is, (it varies on some commands)

    check the boot worked (sending the byte sequence from before)

    getAck sends 0x47 and checks for the shorter CRC’d reply , returning true or false if passed

    repeats for 0xC7 , not sure why it follows this style yet, maybe its a class/subclass thing or another protocol

    now it sends out the 8 byte packet with the message we’re sending, byte 5 = 1 to turn on the buzzer, 0 to turn off the buzzer

    finally it checks to see if the command completed properly, by sending out the getAck(0x7)

    The final getAck will often send back the cmd you sent rather than the CRC, I believe it is doing this as a pause method, in code the retry for the final command to 5 retries, the serial has a 500 ms timeout set. (it is also 115200,n,8,1)

    The 8 byte send is done thru sendData which will tack on the 9th byte which is the computed CRC16/XMODEM/POLY1021 and it never expects a reply for that send

    For the X Y coord’s they take mm and then are n*1000 to convert to an int16, for speed for the rails they are n*2 for the int16

    Building a DLL to snoop

    Even though snooping is trivial on serial, it is often useful to build a DLL that can intercept data. So the easiest one first is the RS232DLL.DLL.. . Their name not mine Smile

    First step is just to take a quick peek at what the DLL exports, dumpbin (MSVC)  will do that.

    dumpbin /exports rs232dll.dll

    File Type: DLL
         Section contains the following exports for rs232.dll
           00000000 characteristics
            545B1F00 time date stamp Wed Nov  5 23:10:56 2014
               0.00 version
                  1 ordinal base
                  5 number of functions
                  5 number of names
           ordinal hint RVA      name
                 1    0 00001080 rs232_close
                  2    1 00001050 rs232_open
                 3    2 00001090 rs232_read
                 4    3 000010B0 rs232_readSyn
                  5    4 000010D0 rs232_write
               5000 .data
                 1000 .rdata
               1000 .reloc
                7000 .text

    Great it is simple, open close, read , another read, and a write.

    Now to make a proxy DLL, there are a bunch of tools to do this, I haven’t tried this one but it has both x64 and x32 which i’d added to the tool used here, the other one is here


    In short a proxy dll when loaded  loads the old dll which has been renamed, and then adds the functions from the old DLL as pointers, when the host calls your proxy dll , you do stuff then pass control to the original DLL


    extern “C” __declspec ( naked ) void __E__0__()

           __asm pushad
           __asm pushfd
           OutputDebugStringA ( “rs232_close\n” );
           __asm popfd
           __asm popad
           __asm {
                jmp procs[E_RS232_CLOSE*4];


    this is the proxy for the rs232_close function. All it does is print rs232_close\n to the windows debug log.

    the DLLMain is where the proxy is setup

           OutputDebugStringA ( “DllMain called\n” );
           if ( reason == DLL_PROCESS_ATTACH ) {
                OutputDebugStringA ( “DllMain DLL_PROCESS_ATTACH\n” );
               hLThis = hInst;
               hL = LoadLibrary ( _T ( “rs232dllOLD.DLL” ) );
               if ( !hL ) { OutputDebugStringA ( “Failed to load original library\n” ); return false; }
                procs[E_RS232_CLOSE] = GetProcAddress ( hL, “rs232_close” );
               if ( procs[E_RS232_CLOSE] == NULL ) { OutputDebugStringA ( “Failed to get proc address rs232_close\n” ); }
               procs[E_RS232_OPEN] = GetProcAddress ( hL, “rs232_open” );
               if ( procs[E_RS232_OPEN] == NULL ) { OutputDebugStringA ( “Failed to get proc address rs232_open\n” ); }
               procs[E_RS232_READ] = GetProcAddress ( hL, “rs232_read” );
               if ( procs[E_RS232_READ] == NULL ) { OutputDebugStringA ( “Failed to get proc address rs232_read\n” ); }
               procs[E_RS232_READSYN] = GetProcAddress ( hL, “rs232_readSyn” );
               if ( procs[E_RS232_READSYN] == NULL ) { OutputDebugStringA ( “Failed to get proc address rs232_readSyn\n” ); }
               procs[E_RS232_WRITE] = GetProcAddress ( hL, “rs232_write” );
               if ( procs[E_RS232_WRITE] == NULL ) { OutputDebugStringA ( “Failed to get proc address rs232_write\n” ); }
           if ( reason == DLL_PROCESS_DETACH ) {
                OutputDebugStringA ( “DllMain DLL_PROCESS_DETACH\n” );
               FreeLibrary ( hL );
           return 1;

    When the HOST attaches to our proxy DLL , it uses LoadLibrary to open the old DLL RS232dllOLDd.dll then it uses GetProcAddress on the handle for that DLL and gets the pointer to the real function, and stores it in an array for later.

    Very basic,  and works great., this is for an X32 proxy with VC unless we want to emit we use an external asm file for X64, which would look like this.

    ; entirelyDifferentFunc

      __E__0__ proc
           lea        rcx, _entirelyDifferentFunc
           call qword ptr    __imp_OutputDebugStringA
           call        procs[0*8]

    __E__0__ endp

    _entirelyDifferentFunc    db    “entirelyDifferentFunc”,13,10,0

    PUSHAQ/POPAQ are macros that save all the things.

    Also use a.def file to map the new functions to the originals, as well define the exports/order

    LIBRARY   rs232dll


    rs232_close @1

    rs232_open @2

    rs232_read @3

    rs232_readSyn @4

    rs232_write @5

    The @number is the ordinal for the function in the DLL, its index. This tells the linker we’re exporting these functions, we can alias them here too. Depending on how the original DLL was built it might be mangled,. or use stdcall/fastcall etc etc so this can be dealt with here

    aliasing looks like this


    rs232_close=__E__0__ @1

    rs232_open=__E__1__ @2

    rs232_read=__E__2__ @3

    rs232_readSyn=__E__3__ @4

    rs232_write=__E__4__ @5

    this allows us to map the autogenerated names to the real names.

    Once you’ve mapped all these out you can make a simple dump function to show the data being passed in and out

    // name of function, ptr to data to dump and length of data

    void dump ( const  char *name, unsigned char *ptr, unsigned int length )

           if ( ptr ) {
               if ( length ) {
                     //if ( ptr[0] != 0x64 )   { return; }
                   if ( name ) {
                        OutputDebugStringA ( name );
                   _RPT0 ( _CRT_WARN, “Write = { ” );
                   while ( length– ) {
                        _RPT1 ( _CRT_WARN, “0x%02x,”, *ptr++ );
                   _RPT0 ( _CRT_WARN, “} ;\n ” );


    Here you can also filter out data you don’t care about, you can see an example commented out where i wasn’t interested in messages that started with 0x64 this can help increase signal to noise ratio

    Forget Snoop, Just rewrite the DLL

    Since the RS232 dll turned out to be really basic, it was just reimplemented as to have total control

    __declspec ( dllexport ) int __cdecl rs232_read ( LPVOID lpBuffer, DWORD nNumberOfBytesToRead )

           DWORD bytesRead = 0;
           BOOL  fSuccess = ReadFile (
                                 hCom,    //Handle
                                lpBuffer,  //Incoming data
                                 &bytesRead, //Bytes Read
                                0 // not overlapped
           dump ( “rs232_read”, ( unsigned char* ) lpBuffer, bytesRead );
           return bytesRead;

    __declspec( dllexport) tells VC that we’re exporting this function

    // doesnt use serial timeouts, uses a GetTickCount

    __declspec ( dllexport ) int __cdecl rs232_readSyn ( PVOID lpBuffer, DWORD nNumberOfBytesToRead, DWORD timeout )

           ULONGLONG endTime;
           DWORD bytesRead = 0;
           endTime = GetTickCount64() + timeout;
           do {
                bytesRead = 0;
               BOOL  fSuccess = ReadFile (
                                    NULL // not overlapped
               if ( fSuccess & bytesRead ) {
                   dump ( “rs232_readSyn”, ( unsigned char* ) lpBuffer, bytesRead );
                   return 1;
           } while ( GetTickCount64() < endTime );
           return 0;

    Mapping these functions out to be as close to the original as possible.

    __declspec ( dllexport ) int __cdecl rs232_write ( LPVOID lpBuffer, DWORD nNumberOfBytesToWrite )
           DWORD dwNumberOfBytesWritten = 0;
           dump ( “rs232_write”, ( unsigned char* ) lpBuffer, nNumberOfBytesToWrite );
           BOOL fSuccess
                = WriteFile (
           return dwNumberOfBytesWritten;


    __declspec ( dllexport ) int __cdecl rs232_close()

           OutputDebugStringA ( “rs232_close\n” );
           int ret = CloseHandle ( hCom );
           hCom = NULL;
           return ret;

    Comm States used and set internally

    int  OurSetCommState ( HANDLE h, DWORD baud )

           DWORD v7;
           struct _DCB DCB;
           DCB.DCBlength = sizeof ( DCB );
            GetCommState ( h, &DCB );
           DCB.BaudRate = baud;
            DCB.ByteSize = 8;             //  data size, xmit and rcv
           DCB.Parity = NOPARITY;      //  parity bit
           DCB.StopBits = ONESTOPBIT;    //  stop bit
           if ( SetCommState ( h, &DCB ) ) {
                return 1;
           v7 = GetLastError();
           return 0;


    int  SetTimeouts ( HANDLE h )
           DWORD error;
           struct _COMMTIMEOUTS CommTimeouts;
           GetCommTimeouts ( h, &CommTimeouts );
           CommTimeouts.ReadTotalTimeoutMultiplier = 0;
             CommTimeouts.ReadTotalTimeoutConstant = 0;
           CommTimeouts.ReadIntervalTimeout = -1;
            CommTimeouts.WriteTotalTimeoutMultiplier = 10;
           CommTimeouts.WriteTotalTimeoutConstant = 1000;
           if ( SetCommTimeouts ( h, &CommTimeouts ) ) {
                return 1;
           error = GetLastError();
            _RPT1 ( _CRT_WARN, “SetTimeouts failed: = %d\n”, error );
           return 0;

    rs232_open function, which is called with the com2: 115200, ,n,8,1 settings , this also sets timeouts etc

    // opens and sets up baud rate etc.

    //a4/a5 = 0 (parity)

    __declspec ( dllexport ) int __cdecl rs232_open ( LPCSTR lpFileName, int baudrate, int bits, int a4, int a5 )

           OutputDebugStringA ( “rs232_open ” );
            OutputDebugStringA ( lpFileName );
            OutputDebugStringA ( “\n” );
           // HANDLE hCom;
            int result = 0;
           //  Open a handle to the specified com port.
            hCom = CreateFileA ( lpFileName,
                                GENERIC_READ | GENERIC_WRITE,
                                0,      //  must be opened with exclusive-access
                                NULL,   //  default security attributes
                                 OPEN_EXISTING, //  must use OPEN_EXISTING
                                 0,      //  not overlapped I/O
                                 NULL ); //  hTemplate must be NULL for comm devices
           if ( hCom == INVALID_HANDLE_VALUE ) {
                //  Handle the error.
               _RPT1 ( _CRT_WARN, “CreateFile failed with error %d.\n”, GetLastError() );
               return result;
           SetCommMask ( hCom, 511u );
            SetupComm ( hCom, 512u, 32u );
           PurgeComm ( hCom, 12u );
           if ( OurSetCommState ( hCom, baudrate ) ) {
               if ( SetTimeouts ( hCom ) ) {
                   EscapeCommFunction ( hCom, 5 );
                   EscapeCommFunction ( hCom, 3 );
                   PurgeComm ( hCom, 0xF );
                   result = 1;
           return result;

    then there is a global for the HANDLE to the serial device/COM2 

    static HANDLE hCom = 0;

    And then the whole thing is wrapped in, this matches it to the original DLL which says it was built in VC6 ? linker 6.0

    extern “C” {


    rs232_readSyn has the timeout that reads the single ack back

    So in this case it was just as easy to rewrite the DLL as proxying it and ignore the COM settings and fill in as needed

    Adding USB cameras, for some reason

    One of the first things to do with the software after making it run on a dev box was hook up a couple of USB cameras so the machine didn’t need to be up and running while fiddling with it. The Neoden cameras are Cypress FX devices which do basically just have a couple of routines to set exposure, flash, size etc the capture routine just returns a w*h uint8 grey buffer for the image, with the cameras id of 0/1(5) passed to the DLL

    same procedure as before, examine the dll and see what it exports

    ordinal hint RVA      name
             1    0 00001840 img_capture
               2    1 000017D0 img_init
              3    2 00001870 img_led
              4    3 000019F0 img_read
              5    4 00001A30 img_readAsy
              6    5 000018B0 img_reset
              7    6 000018F0 img_set_exp
              8    7 00001930 img_set_gain
              9    8 00001970 img_set_lt
             10    9 000019B0 img_set_wh

    which more or less translates too

    __declspec ( dllexport ) BOOL _cdecl img_capture ( int which_camera);
         __declspec ( dllexport ) int _cdecl img_init();
        __declspec ( dllexport ) BOOL _cdecl img_led ( int which_camera, int16_t mode );
        __declspec ( dllexport ) int _cdecl img_read ( int which_camera, unsigned char * pFrameBuffer, uint32_t BytesToRead, uint32 ms );
        __declspec ( dllexport ) int _cdecl img_readAsy (int which_camera, unsigned char * pFrameBuffer, uint32 BytesToRead, uint32 ms);
        __declspec (dllexport) int _cdecl img_reset(int which_camera);
        __declspec ( dllexport ) BOOL _cdecl img_set_exp ( int which_camera, int16_t exposure );
        __declspec ( dllexport ) BOOL _cdecl img_set_gain ( int which_camera, int16_t gain );
        __declspec ( dllexport ) BOOL _cdecl img_set_lt ( int which_camera, int16_t a2, int16_t a3 );
        __declspec ( dllexport ) BOOL _cdecl img_set_wh ( int which_camera, int16_t w, int16_t h );

    so after wrapping videoinput/opencv into a new dll

    static int16_t g_width, g_height;
        VideoCapture *stream1 = NULL, *stream2 = NULL;
           __declspec ( dllexport ) int _cdecl img_init()
                static bool init = false;
               if ( init == false ) {
                   stream1 = new VideoCapture ( 1 );
                     stream2 = new VideoCapture ( 0 );
                    init = true;
               return 1;

    the software only seemed to use readAsy

    // a1 = 5 (1 if downlooking?)
           // a2 = 0x05580030
          // a3 = 0x00100000
          // a5 = 0x97 (changes)
          // dwMilliseconds = 1000
          int _cdecl img_readAsy ( int which_camera, unsigned char * pFrameBuffer, int a3, DWORD dwMilliseconds, char a5 )
              if ( pFrameBuffer == NULL ) {
                  return 0;
             Mat cameraFrame;
             if ( which_camera == 5 ) {
                   if ( stream1 == NULL ) {
                      return 0;
                 stream1->read ( cameraFrame );
             } else {
                   if ( stream2 == NULL ) {
                      return 0;
                 stream2->read ( cameraFrame );
                 // my camera was flipped
                   flip ( cameraFrame, cameraFrame, 0 );
            // convert to grey
              if ( cameraFrame.rows ) {
                  cv::Mat greyMat;
                  flip ( cameraFrame, cameraFrame, 1 );
                  cv::cvtColor ( cameraFrame, greyMat, CV_BGR2GRAY );
                  Size size ( g_width, g_height );
                  resize ( greyMat, greyMat, size );
                 memcpy ( pFrameBuffer, greyMat.ptr(), g_height * g_width );
              return 1;

    // a2 = 1024, a3 =1024 a1 = 5

    BOOL _cdecl img_set_wh ( int which, int16_t width, int16_t height )

           _RPT3 ( _CRT_WARN, “img_set_wh) %d %d %d\n”, which, width, height );
           g_width = width;
           g_height = height;
           return TRUE;

    So just a very quick and dirty interface to cameras that VideoInput supports, it is only really useful during desktop RE work but maybe there will be a time when i can swap out the cameras in the machine, either way it works and we can move on to the real Neoden cameras

    Deeper Dive with the Neoden Camera

    Picking apart the NeodenCamera.dll it is using CyApi / CyUsb3.sys to communicate with the cameras. I did see a post on eevblog that said it was CGUSB2.dll there might be an older rev of the hardware that uses those cameras, but these don’t. Similar hardware so could just be a rewrite to the new FX chip

    Going over the IOCTLs

    First process is to build up the Constructor/Destructor code for the CyUSB interface, since that shows me where the device context/structs are. Also decoding the IOCTLs which are built with

    #define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

    It creates a 32 bit value

    diagram illustrating the i/o control code layout

    Online Decoder

    The commonly appearing IOCTL is 0x220020 which decodes as

    Device = 0x22(FILE_DEVICE_UNKNOWN) which means this device doesn’t match any of the predefined ones

    Function 0x8


    Method unbuffered

    The others observed are

    0x220008 Function 0x08 Unbuffered

    0x22003C Function 0xF Buffered

    0x220040  Function 0x10 Buffered

    0x220010 Function 0x04 Buffered

    0x220000 Function 0x0 Buffered

    0x220004 Function 0x01 Buffered

    Looking in cyioctl.h from the Cypress SDK the IOCTLS are defined as


    IOCTL_ADAPT_INDEX as 0x000

    So lets see what matches and if it makes any sense

    Taking the IOCTL 0x220000 we see that is function 0, buffered, so seems to match IOCTL_ADAPT_GET_DRIVER_VERSION  so now to check the code and see if it looks like that IOCTL is indeed looking for a driver version, this seems like a lucky place to start since a driver version is going to be obvious, Plus they are all in the Open call so filling in the basic startup stuff

    void  CallDeviceIO_220000(DWORD *HANDLE)

          DWORD *pThis;
          DWORD *v2;

       pThis = HANDLE;
          if ( HANDLE[541] == -1 )                      // handle to device, is it not open?
            HANDLE[11] = 0; // otherwise set output buffer to \0
            outbutBuffer = HANDLE + 11;

              // handle, ioctl, ptr to outbuffer, size of buffer
            if ( !CallDeviceIO(HANDLE, 0x220000u, HANDLE + 11, 4u) || !pThis[530] )                                            

    // failed
              *outputBuffer = 0;


    So it is asking for 4 bytes from the device, Lets look at the others

    220000 Function 0x0 Buffered IOCTL_ADAPT_GET_DRIVER_VERSION

    220004 Function 0x01 Buffered  IOCTL_ADAPT_GET_USBDI_VERSION Get the USBDI Version

    220010 Function 0x04 Buffered IOCTL_ADAPT_GET_ADDRESS Get device address from driver

    220008 Function 0x08 Unbuffered IOCTL_ADAPT_SEND_EP0_CONTROL_TRANSFER Send a raw packet to endpoint

    220040 Function 0x10 Buffered IOCTL_ADAPT_GET_FRIENDLY_NAME

    22003C Function 0xF Buffered IOCTL_ADAPT_GET_DEVICE_NAME

    Confirm with CyUSB3/CyAPI SDK

    Seems to make sense, looking back at the code  the IOCTLs that pass strings have larger buffers, (0x100) but before we go too far down the rabbit hole, lets look back at the Cypress SDK

    And there it is in the Open function







    Great everything is matching, so now have verified this is the right SDK and it is acting as expected.

    bool CCyUSBDevice::Open(UCHAR dev)

    Calling this function and from the SDK know that the HANDLE above is pointing to this

    typedef struct _USB_DEVICE_DESCRIPTOR {
            UCHAR   bLength;
            UCHAR   bDescriptorType;
            USHORT  bcdUSB;
            UCHAR   bDeviceClass;
            UCHAR   bDeviceSubClass;
            UCHAR   bDeviceProtocol;
            UCHAR   bMaxPacketSize0;
            USHORT  idVendor;
            USHORT  idProduct;
            USHORT  bcdDevice;
            UCHAR   iManufacturer;
            UCHAR   iProduct;
            UCHAR   iSerialNumber;
            UCHAR   bNumConfigurations;


    So at this point  it might be best to start rebuilding the NeodenCamera.dll with the CyUSB3 SDK, since again it is a simple enough DLL with just the specifics for chatting to the hardware.

    Can we recreate the NeodenCamera.dll

    Just to be clear, This doesn’t really need to be done this since how to use the existing DLL is now documented ,but for the *nix people they can recreate with CyAPi or libusb.

    Loaded up VC2017,  created a DLL project, set it to MBCS added the CyUsb3 files to the project, turned off Precompiled headers for CyAPi.cpp  , added stdint.h and crtdbg.h to the stdafx.h , also need SetupAPI.lib for CyUsb3 added that to stdafx.h as well. Currently its set to be an X86 since the Neoden software is all 32 bit.

    Using the information from the USB VideoCapture driver and filling out the basic functions that the DLL exports.

    __declspec ( dllexport ) BOOL _cdecl img_capture ( int which_camera );

    __declspec ( dllexport ) int _cdecl img_init();

    __declspec ( dllexport ) BOOL _cdecl img_led ( int which_camera, int16_t mode );

    __declspec ( dllexport ) int _cdecl img_read ( int which_camera, unsigned char * pFrameBuffer, int BytesToRead, int ms);

    __declspec ( dllexport ) int _cdecl img_readAsy ( int which_camera, unsigned char * pFrameBuffer, int BytesToRead, int ms);

    __declspec ( dllexport ) int _cdecl img_reset ( int which_camera );

    __declspec ( dllexport ) BOOL _cdecl img_set_exp ( int which_camera, int16_t exposure );

    __declspec ( dllexport ) BOOL _cdecl img_set_gain ( int which_camera, int16_t gain );

    __declspec ( dllexport ) BOOL _cdecl img_set_lt ( int which_camera, int16_t a2, int16_t a3 );

    __declspec ( dllexport ) BOOL _cdecl img_set_wh ( int which_camera, int16_t w, int16_t h );

    They’re a little rough at the moment but they will be fixed up as work progresses

    Forgot to add the .def so thats just (although the code is using the __declspec(dllexport)


    img_capture = img_capture @1

    img_init @2

    img_led @3

    img_read @4

    img_readAsy @5

    img_reset @6

    img_set_exp @7

    img_set_gain @8

    img_set_lt @9

    img_set_wh @10

    Next step is setting up the USB Device, so add CyUSB.h to stdafx.h and add a couple of CCyUSNDevice to init the USB

    So lets enumerate the USB devices that match ours first. Also at this point I realise its easier to run this as an EXE so i change the type from DLL to EXE in the VC options, and add a WInMain

    int WINAPI WinMain (
            HINSTANCE hInstance,    
            HINSTANCE hPrevInstance,
            LPSTR lpCmdLine,        
            int nCmdShow      


            return 0;


    Now i can just run as an EXE don’t need a host to test.

    Enumerating the USB

    Ok lets enumerate the USB devices we want

    // Create the CyUSBDevice

    CCyUSBDevice* USBNeodenCamera = new CCyUSBDevice ( 0, CYUSBDRV_GUID, true );

    int n = USBNeodenCamera->DeviceCount();

    // for all Cypress devices found

    for ( int i = 0; i < n; i++ ) {

        USBNeodenCamera->Open ( i );

        // Is it the Neoden?
            if ( USBNeodenCamera->VendorID != 0x52CB ) {

         std::string model = narrow ( std::wstring ( USBNeodenCamera->Product ) );
            std::string serial = narrow ( std::wstring ( USBNeodenCamera->SerialNumber ) );

        _RPT5 ( _CRT_WARN, “DID %04x:%04x, %s, %s, %s,%s\n”,
                    USBNeodenCamera->VendorID, USBNeodenCamera->ProductID,
                    USBNeodenCamera->DeviceName, USBNeodenCamera->DevPath,
                    model.c_str(), serial.c_str()


    delete USBNeodenCamera;

    Then test.

    DID 52cb:52cb, B0001 Camera Shibz, \\?\usb#vid_52cb&pid_52cb#1&2d12bed1&0&0000#{ae18aa60-7f6a-11d4-97dd-00010229b959}, B0001 Camera Shibz,

    DID 52cb:52cb, H0001 Camera Shibz, \\?\usb#vid_52cb&pid_52cb#1&2d12bed1&0&0001#{ae18aa60-7f6a-11d4-97dd-00010229b959}, H0001 Camera Shibz,

    Perfect, both cameras found. the 0000 and 0001 are up and down cameras . both use the same driver

    Since this is a simple DLL and that it is known that both cameras exist , that are always two. That does reduce the complexity a bit, so it can be noted either by the device index or init a global to point to each of the cameras when we find them. Shibz is slang for Shibuya, Tokyo not sure if there is another clue there or not. The names of the cameras do differ slightly with a H and B

    After the enumeration, I figure that the img_led function is probably easiest to implement to start off with, poking through the original dll i see another IOCTL

    0x220044 which decodes as Function 0x11, referring to the Cyioctl.h 0x11 is IOCTL_ADAPT_ABORT_PIPE, which aborts any current EndPoint transactions, a purge if you will, it’s another one of the CyApis functions

    RetVal = (DeviceIoControl(hDevice,IOCTL_ADAPT_ABORT_PIPE,&Address,sizeof(UCHAR),NULL,0,&dwBytes,&ov)!=0);

    So now we’ll need an EndPoint, time to dig some more. Lets pop up USBDeview on the machine

    Wall of text time. Figure out the EndPoints

    First Camera

        =========================== USB Port1 ===========================
    Connection Status        : 0x01 (Device is connected)
    Port Chain               : 5-1
    PortAttributes           : 0x00000002 (Shared USB2)
          ======================== USB Device ========================
            +++++++++++++++++ Device Information ++++++++++++++++++
    Device Description       : Camera Neoden Tech.
    Device Path              : \\?\usb#vid_52cb&pid_52cb#5&1783ac8f&0&1#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
    Device ID                : USB\VID_52CB&PID_52CB\5&1783AC8F&0&1
    Hardware IDs             : USB\Vid_52cb&Pid_52cb&Rev_0001 USB\Vid_52cb&Pid_52cb
    Driver KeyName           : {36FC9E60-C465-11CF-8056-444553540000}\0014 (GUID_DEVCLASS_USB)
    Driver                   : System32\Drivers\CYUSB3.sys (Version:  Date: 2014-09-18)
    Driver Inf               : C:\WINDOWS\inf\oem1.inf
    Legacy BusType           : PNPBus
    Class                    : USB
    Class GUID               : {36FC9E60-C465-11CF-8056-444553540000} (GUID_DEVCLASS_USB)
    Interface GUID           : {a5dcbf10-6530-11d2-901f-00c04fb951ed} (GUID_DEVINTERFACE_USB_DEVICE)
    Service                  : CYUSB3
    Enumerator               : USB
    Location Info            : B0001 Camera Shibz
    Manufacturer Info        : Cypress
    Capabilities             : 0x84 (Removable, SurpriseRemovalOK)
    Problem Code             : 0
    Address                  : 1
    Power State              : D0 (supported: D0, D3, wake from D0, wake from D3)
            +++++++++++++++++ Registry USB Flags +++++++++++++++++
     GlobalDisableSerNumGen  : REG_BINARY 01
     osvc                    : REG_BINARY 00 00
            ---------------- Connection Information ---------------
    Connection Index         : 0x01 (1)
    Connection Status        : 0x01 (DeviceConnected)
    Current Config Value     : 0x01
    Device Address           : 0x01 (1)
    Is Hub                   : 0x00 (no)
    Number Of Open Pipes     : 0x01 (1)
    Device Bus Speed         : 0x02 (High-Speed)
    Pipe0ScheduleOffset      : 0x00 (0)
    Data (HexDump)           : 01 00 00 00 12 01 00 01 00 00 00 40 CB 52 CB 52   ...........@.R.R
                               01 00 01 02 00 01 01 02 00 01 00 01 00 00 00 01   ................
                               00 00 00 07 05 82 02 00 02 00 00 00 00 00         ..............
        ---------------------- Device Descriptor ----------------------
    bLength                  : 0x12 (18 bytes)
    bDescriptorType          : 0x01 (Device Descriptor)
    bcdUSB                   : 0x100 (USB Version 1.00)
    bDeviceClass             : 0x00 (defined by the interface descriptors)
    bDeviceSubClass          : 0x00
    bDeviceProtocol          : 0x00
    bMaxPacketSize0          : 0x40 (64 bytes)
    idVendor                 : 0x52CB
    idProduct                : 0x52CB
    bcdDevice                : 0x0001
    iManufacturer            : 0x01 (String Descriptor 1)
     Language 0x0409         : "Neoden HangZhou"
    iProduct                 : 0x02 (String Descriptor 2)
     Language 0x0409         : "B0001 Camera Shibz"
    iSerialNumber            : 0x00 (No String Descriptor)
    bNumConfigurations       : 0x01 (1 Configuration)
    Data (HexDump)           : 12 01 00 01 00 00 00 40 CB 52 CB 52 01 00 01 02   .......@.R.R....
                               00 01                                             ..
        ------------------ Configuration Descriptor -------------------
    bLength                  : 0x09 (9 bytes)
    bDescriptorType          : 0x02 (Configuration Descriptor)
    wTotalLength             : 0x0019 (25 bytes)
    bNumInterfaces           : 0x01 (1 Interface)
    bConfigurationValue      : 0x01 (Configuration 1)
    iConfiguration           : 0x00 (No String Descriptor)
    bmAttributes             : 0x80
     D7: Bus Powered         : 0x01 (yes)
     D6: Self Powered        : 0x00 (no)
     D5: Remote Wakeup       : 0x00 (no)
     D4..0: Reserved, set 0  : 0x00
    MaxPower                 : 0x32 (100 mA)
    Data (HexDump)           : 09 02 19 00 01 01 00 80 32 09 04 00 00 01 FF 00   ........2.......
                               00 00 07 05 82 02 00 02 00                        .........
            ---------------- Interface Descriptor -----------------
    bLength                  : 0x09 (9 bytes)
    bDescriptorType          : 0x04 (Interface Descriptor)
    bInterfaceNumber         : 0x00
    bAlternateSetting        : 0x00
    bNumEndpoints            : 0x01 (1 Endpoint)
    bInterfaceClass          : 0xFF (Vendor Specific)
    bInterfaceSubClass       : 0x00
    bInterfaceProtocol       : 0x00
    iInterface               : 0x00 (No String Descriptor)
    Data (HexDump)           : 09 04 00 00 01 FF 00 00 00                        .........
            ----------------- Endpoint Descriptor -----------------
    bLength                  : 0x07 (7 bytes)
    bDescriptorType          : 0x05 (Endpoint Descriptor)
    bEndpointAddress         : 0x82 (Direction=IN EndpointID=2)
    bmAttributes             : 0x02 (TransferType=Bulk)
    wMaxPacketSize           : 0x0200 (max 512 bytes)
    bInterval                : 0x00 (never NAKs)
    Data (HexDump)           : 07 05 82 02 00 02 00                              .......
          -------------------- String Descriptors -------------------
                 ------ String Descriptor 0 ------
    bLength                  : 0x04 (4 bytes)
    bDescriptorType          : 0x03 (String Descriptor)
    Language ID[0]           : 0x0409 (English - United States)
    Data (HexDump)           : 04 03 09 04                                       ....
                 ------ String Descriptor 1 ------
    bLength                  : 0x20 (32 bytes)
    bDescriptorType          : 0x03 (String Descriptor)
    Language 0x0409          : "Neoden HangZhou"
    Data (HexDump)           : 20 03 4E 00 65 00 6F 00 64 00 65 00 6E 00 20 00    .N.e.o.d.e.n. .
                               48 00 61 00 6E 00 67 00 5A 00 68 00 6F 00 75 00   H.a.n.g.Z.h.o.u.
                 ------ String Descriptor 2 ------
    bLength                  : 0x26 (38 bytes)
    bDescriptorType          : 0x03 (String Descriptor)
    Language 0x0409          : "B0001 Camera Shibz"
    Data (HexDump)           : 26 03 42 00 30 00 30 00 30 00 31 00 20 00 43 00   &.B. .C.
                               61 00 6D 00 65 00 72 00 61 00 20 00 53 00 68 00   a.m.e.r.a. .S.h.
                               69 00 62 00 7A 00                                 i.b.z.

    Second camera

        =========================== USB Port8 ===========================
    Connection Status        : 0x01 (Device is connected)
    Port Chain               : 5-8
    PortAttributes           : 0x00000002 (Shared USB2)
          ======================== USB Device ========================
            +++++++++++++++++ Device Information ++++++++++++++++++
    Device Description       : Camera Neoden Tech.
    Device Path              : \\?\usb#vid_52cb&pid_52cb#5&1783ac8f&0&8#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
    Device ID                : USB\VID_52CB&PID_52CB\5&1783AC8F&0&8
    Hardware IDs             : USB\Vid_52cb&Pid_52cb&Rev_0001 USB\Vid_52cb&Pid_52cb
    Driver KeyName           : {36FC9E60-C465-11CF-8056-444553540000}\0015 (GUID_DEVCLASS_USB)
    Driver                   : System32\Drivers\CYUSB3.sys (Version:  Date: 2014-09-18)
    Driver Inf               : C:\WINDOWS\inf\oem114.inf
    Legacy BusType           : PNPBus
    Class                    : USB
    Class GUID               : {36FC9E60-C465-11CF-8056-444553540000} (GUID_DEVCLASS_USB)
    Interface GUID           : {a5dcbf10-6530-11d2-901f-00c04fb951ed} (GUID_DEVINTERFACE_USB_DEVICE)
    Service                  : CYUSB3
    Enumerator               : USB
    Location Info            : H0001 Camera Shibz
    Manufacturer Info        : Cypress
    Capabilities             : 0x84 (Removable, SurpriseRemovalOK)
    Problem Code             : 0
    Address                  : 8
    Power State              : D0 (supported: D0, D3, wake from D0, wake from D3)
            +++++++++++++++++ Registry USB Flags +++++++++++++++++
     GlobalDisableSerNumGen  : REG_BINARY 01
     osvc                    : REG_BINARY 00 00
            ---------------- Connection Information ---------------
    Connection Index         : 0x08 (8)
    Connection Status        : 0x01 (DeviceConnected)
    Current Config Value     : 0x01
    Device Address           : 0x04 (4)
    Is Hub                   : 0x00 (no)
    Number Of Open Pipes     : 0x01 (1)
    Device Bus Speed         : 0x02 (High-Speed)
    Pipe0ScheduleOffset      : 0x00 (0)
    Data (HexDump)           : 08 00 00 00 12 01 00 01 00 00 00 40 CB 52 CB 52   ...........@.R.R
                               01 00 01 02 00 01 01 02 00 04 00 01 00 00 00 01   ................
                               00 00 00 07 05 82 02 00 02 00 00 00 00 00         ..............
        ---------------------- Device Descriptor ----------------------
    bLength                  : 0x12 (18 bytes)
    bDescriptorType          : 0x01 (Device Descriptor)
    bcdUSB                   : 0x100 (USB Version 1.00)
    bDeviceClass             : 0x00 (defined by the interface descriptors)
    bDeviceSubClass          : 0x00
    bDeviceProtocol          : 0x00
    bMaxPacketSize0          : 0x40 (64 bytes)
    idVendor                 : 0x52CB
    idProduct                : 0x52CB
    bcdDevice                : 0x0001
    iManufacturer            : 0x01 (String Descriptor 1)
     Language 0x0409         : "Neoden HangZhou"
    iProduct                 : 0x02 (String Descriptor 2)
     Language 0x0409         : "H0001 Camera Shibz"
    iSerialNumber            : 0x00 (No String Descriptor)
    bNumConfigurations       : 0x01 (1 Configuration)
    Data (HexDump)           : 12 01 00 01 00 00 00 40 CB 52 CB 52 01 00 01 02   .......@.R.R....
                               00 01                                             ..
        ------------------ Configuration Descriptor -------------------
    bLength                  : 0x09 (9 bytes)
    bDescriptorType          : 0x02 (Configuration Descriptor)
    wTotalLength             : 0x0019 (25 bytes)
    bNumInterfaces           : 0x01 (1 Interface)
    bConfigurationValue      : 0x01 (Configuration 1)
    iConfiguration           : 0x00 (No String Descriptor)
    bmAttributes             : 0x80
     D7: Bus Powered         : 0x01 (yes)
     D6: Self Powered        : 0x00 (no)
     D5: Remote Wakeup       : 0x00 (no)
     D4..0: Reserved, set 0  : 0x00
    MaxPower                 : 0x32 (100 mA)
    Data (HexDump)           : 09 02 19 00 01 01 00 80 32 09 04 00 00 01 FF 00   ........2.......
                               00 00 07 05 82 02 00 02 00                        .........
            ---------------- Interface Descriptor -----------------
    bLength                  : 0x09 (9 bytes)
    bDescriptorType          : 0x04 (Interface Descriptor)
    bInterfaceNumber         : 0x00
    bAlternateSetting        : 0x00
    bNumEndpoints            : 0x01 (1 Endpoint)
    bInterfaceClass          : 0xFF (Vendor Specific)
    bInterfaceSubClass       : 0x00
    bInterfaceProtocol       : 0x00
    iInterface               : 0x00 (No String Descriptor)
    Data (HexDump)           : 09 04 00 00 01 FF 00 00 00                        .........
            ----------------- Endpoint Descriptor -----------------
    bLength                  : 0x07 (7 bytes)
    bDescriptorType          : 0x05 (Endpoint Descriptor)
    bEndpointAddress         : 0x82 (Direction=IN EndpointID=2)
    bmAttributes             : 0x02 (TransferType=Bulk)
    wMaxPacketSize           : 0x0200 (max 512 bytes)
    bInterval                : 0x00 (never NAKs)
    Data (HexDump)           : 07 05 82 02 00 02 00                              .......
          -------------------- String Descriptors -------------------
                 ------ String Descriptor 0 ------
    bLength                  : 0x04 (4 bytes)
    bDescriptorType          : 0x03 (String Descriptor)
    Language ID[0]           : 0x0409 (English - United States)
    Data (HexDump)           : 04 03 09 04                                       ....
                 ------ String Descriptor 1 ------
    bLength                  : 0x20 (32 bytes)
    bDescriptorType          : 0x03 (String Descriptor)
    Language 0x0409          : "Neoden HangZhou"
    Data (HexDump)           : 20 03 4E 00 65 00 6F 00 64 00 65 00 6E 00 20 00    .N.e.o.d.e.n. .
                               48 00 61 00 6E 00 67 00 5A 00 68 00 6F 00 75 00   H.a.n.g.Z.h.o.u.
                 ------ String Descriptor 2 ------
    bLength                  : 0x26 (38 bytes)
    bDescriptorType          : 0x03 (String Descriptor)
    Language 0x0409          : "H0001 Camera Shibz"
    Data (HexDump)           : 26 03 48 00 30 00 30 00 30 00 31 00 20 00 43 00   &.H. .C.
                               61 00 6D 00 65 00 72 00 61 00 20 00 53 00 68 00   a.m.e.r.a. .S.h.
                               69 00 62 00 7A 00                                 i.b.z.

    Thats a lot of data, but there are the endpoints, scanning with a quick cheat with the CyUsb API just running around the endpoints til it was valid, but that doesn’t give me the data as fast as this does.

    Sniffing DeviceIO using a simplified Test App

    Next thing to try is sniffing the DeviceIO going to the cameras, but there will likely be a lot of noise and the Neoden test app doesn’t seem to succesfully grab any data, so lets make a quick app, new project  in VS2017 again. Making a console app


    Added the videoinput/opencv libs and headers for video capture, and create a simple image display

    #include <iostream>

    #include <stdint.h>

    #include <stdlib.h>

    #include “opencv2/highgui/highgui.hpp”

    #include “opencv2/imgproc/imgproc.hpp”

    #include “opencv2/opencv.hpp”

    #pragma comment(lib,”comctl32.lib”)

    #pragma comment(lib,”gdi32.lib”)

    #pragma comment(lib,”vfw32.lib”)

    using namespace cv;

    using namespace std;

    int main()

            int g_width = 1024, g_height = 1024;

        Mat cameraFrame ( g_width, g_height, CV_8UC3, Scalar ( 10, 100, 150 ) );;
            cv::Mat greyMat;
            flip ( cameraFrame, cameraFrame, 1 );
            cv::cvtColor ( cameraFrame, greyMat, CV_BGR2GRAY );
            Size size ( g_width, g_height );
            resize ( greyMat, greyMat, size );

        imshow ( “camera”, greyMat );

        cv::waitKey ( 0 )


    Creating a .lib and .h for a DLL

    Next step is linking to the NeodenCamera.dll which we can do with LoadLibrary or create a header and .lib so lets do that.

    dumpbin /exports NeodenCamera.dll

    take these

       1    0 00001840 img_capture
         2    1 000017D0 img_init
         3    2 00001870 img_led
         4    3 000019F0 img_read
         5    4 00001A30 img_readAsy
         6    5 000018B0 img_reset
         7    6 000018F0 img_set_exp
         8    7 00001930 img_set_gain
         9    8 00001970 img_set_lt
        10   9 000019B0 img_set_wh

    convert it to

    LIBRARY NeodenCamera.dll


    save it as NeodenCamera.def, from VC folder use  (run vcvars32.bat)

    lib /def:NeodenCamera.def /out:NeodenCamera.lib /machine:x86

    This makes the .lib and .exp file, we just need the .LIB, add it to your project.

    Finally we need the prototypes for the function, IDA or such can help with that. Typically these sorts of DLLs  use stack based argument passing as in standard C so they’re easier to figure out. if its C++ and they’re using mangled names you can demangle for at least the basics. There are lots of tutorials on IDA so rather than focus there i’ll just kickstart with what is known so far

    bool _cdecl img_capture ( int which_camera );

    int _cdecl img_init();

    bool _cdecl img_led ( int which_camera, int16_t mode );

    int _cdecl img_read ( int which_camera, unsigned char * pFrameBuffer, int BytesToRead, int dwMilliseconds);

    int _cdecl img_readAsy ( int which_camera, unsigned char * pFrameBuffer, int BytesReturned, int dwMilliseconds);

    int _cdecl img_reset ( int which_camera );

    bool _cdecl img_set_exp ( int which_camera, int16_t exposure );

    bool _cdecl img_set_gain ( int which_camera, int16_t gain );

    bool _cdecl img_set_lt ( int which_camera, int16_t a2, int16_t a3 );

    bool _cdecl img_set_wh ( int which_camera, int16_t w, int16_t h );

    these are C linkage, the may have to be set to extern “C”  depending on the setup

    Starting here, calling img_init() and noting the results . Running the app you should get a warning about the NeodenCamera.dll missing which is expected, so add the dll to the path or to the same folder as the test exe

    calling img_init()

    bool ret = img_init();

    printf ( “%d = img_ini\nt”, ret );



    ╔Φ▒╕┤·║┼[0]  = B0001 Camera Shibz
    ╔Φ▒╕┤·║┼[1]  = H0001 Camera Shibz

    1 = img_init

    so far so good, but since that  that is a function with no parameters and hard to mess up. but it means the .def and .lib worked

    Lets try to read the image from camera 5 (UP)

    unsigned char buffer[ 1024 * 1024  ];

    int main()

           int g_width = 1024, g_height = 1024;
           DWORD bytesToRead= g_width * g_height;
           bool ret = img_init();
           memset ( &buffer[0], 0xaa, sizeof ( buffer ) );
           printf ( “%d = img_init\n”, ret );
           // camera indices are to be 1 and 5(looking up)
           ret = img_readAsy ( 5, &buffer[0], bytesToRead, 1000 );
           printf ( “%d = img_readAsy, %d\n”, ret, bytesToRead);
           Mat cameraFrame ( Size ( g_width, g_height ), CV_8UC1, buffer, 1024 );
           imshow ( “camera”, cameraFrame );

          cv::waitKey ( 0 );



    Great there is is the UP camera, I also think we have to init the width and height first. but after a run of the neoden software first and the settings stayed.

    Lets try the down camera next, switch the 5 to 1



    Next add the width and height set, which seems to work

    int main()

            int g_width = 1024, g_height = 1024;
            DWORD bytesToRead = g_width * g_height;

        memset ( &buffer[0], 0xaa, sizeof ( buffer ) );

        bool ret = img_init();
          printf ( “%d = img_init\n”, ret );

        ret = img_set_wh ( 1, g_width, g_height );
          printf ( “%d = img_set_wh(1,w,h)\n”, ret );

        ret = img_set_wh ( 5, g_width, g_height );
          printf ( “%d = img_set_wh(5,w,h)\n”, ret );

        // camera indices are to be 1 and 5(looking up)
          ret = img_readAsy ( 1, &buffer[0], bytesToRead, 1000 );
          printf ( “%d = img_readAsy, %d\n”, ret, bytesToRead );

        Mat cameraFrame ( Size ( g_width, g_height ), CV_8UC1, buffer, 1024 );

        imshow ( “camera”, cameraFrame );

        cv::waitKey ( 0 );


    Thats enough to get started, the simpler the better.

    Sniffing DeviceIO

    The PNP software starts up with

    img_set_exp 1 25
    img_set_gain) 1 8
    img_set_lt 1 624 496
    img_set_wh 1 32 32
    img_set_exp 5 100
    img_set_lt 5 128 0
    img_set_wh 5 1024 1024

    Previously it wasn’t noted that the software calls init twice. also odd size for camera 1

    From the test app

    img_set_exp) 1 25
    img_set_gain) 1 8
    img_set_lt) 1 128 0
    img_set_wh) 1 1024 1024
    img_readAsy) 1 0x30d0030 1048576 1000 ms

    Which all matches

    Another great tool for spying on API’s is API Monitor using the X32 version and turning off everything but DeviceIoControl then running the test appl, clearing out the trace until the image appears, then switching the camera we see the following

    Use the binoculars to bring up the search after loading X32 version of API monitor


    Search for DeviceIOControl (case insensitive) and then check them off in the API Filter


    Then either use monitor new process or enable the capture of newly launched processes and it’ll ask to monitor the new process


    Now run Neoden4 software or the TestApp previously made ( add published to github link)


    Then API Monitor shows you the filtered functions we’re interested in so press the various manual test functions to see the results, it’ll also show the buffers being passed and the parameters.

    DeviceIoControl ( 0x000000b8, 0x220024, 0x00c00020, 524326, 0x00c00020, 524326, 0x0018fc34, 0x0018fc98 )

    FALSE    997 = Overlapped I/O operation is in progress.     0.0000831

    DeviceIoControl ( 0x000000b8, 0x220024, 0x03840020, 524326, 0x03840020, 524326, 0x0018fc34, 0x0018fcac )

    FALSE    997 = Overlapped I/O operation is in progress.     0.0000560

    DeviceIoControl ( 0x000000b8, 0x00220020 , 0x002ca348, 44, 0x002ca348, 44, 0x0018fbd0, 0x0018fbf8 )
    FALSE    997 = Overlapped I/O operation is in progress.     0.0000057

    for img_led(5,0)

    DeviceIoControl ( 0x000000bc, 0x00220020  , 0x0310a348, 44, 0x0310a348, 44, 0x0063fc54, 0x0063fc7c )

    FALSE    997 = Overlapped I/O operation is in progress.     0.0000213
    0000  40 b2 00 00 00 00 06 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 26 00 00 00 0022  06 00 00 00 b2 01 00 00 00 00        

    for img_led(5,1)

    DeviceIoControl ( 0x000000bc,0x00220020    , 0x0310a348, 44, 0x0310a348, 44, 0x0063fc54, 0x0063fc7c )

    FALSE    997 = Overlapped I/O operation is in progress.     0.0000239
    0000  40 b2 01 00 00 00 06 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 26 00 00 00 0022  06 00 00 00 b2 01 01 00 00 00       

    Marked the differences, i’m not sure if img_led is used, or if i’m using it right anyway these camera boards tend to have a cypress FX CY7C68013 and either an fpga (if they’re UVC or need a lot of processing) or straight hooked to the GPIOs to do triggers, LED flash etc. So that could be where they are or it could be attached from the RS232 and the motor control board.

    The Flash LEDs are not controlled via the camera dll, i think they’re a throwback to another version since we know the LEDs are connected to the head motor control board and controlled via CAN bus from the primary motor control board. They are included in my test harness from for serial control.

    As a note be aware of the EP0 control since that also controls how the FX is programmed and erased

    For capture of up(5)

    DeviceIoControl ( 0x000000bc, 0x00220024, 0x03840020, 524326, 0x03840020, 524326, 0x0063fb84, 0x0063fbe8 )

    FALSE    997 = Overlapped I/O operation is in progress.     0.0000777

    DeviceIoControl ( 0x000000bc, 0x00220024, 0x03960020, 524326, 0x03960020, 524326, 0x0063fb84, 0x0063fbfc )
        FALSE    997 = Overlapped I/O operation is in progress.     0.0000560

    Partial Buffer

    0000  00 00 00 00 00 00 00 00 00 00 00 00 00 82 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 26 00 00 00
    0022  00 00 08 00 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01
    0044  01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01
    0066  01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 00 01 01 01 00 01 00 01 01 01 01 01 01 01 01 01 01 01
    0088  00 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01
    00aa  01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
    00cc  01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
    00ee  01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 01 01 01 01 01 00 01 00 01 00 01

    DeviceIoControl ( 0x000000bc, 0x00220020  , 0x0310a348, 44, 0x0310a348, 44, 0x0063fb20, 0x0063fb48 ) 

    FALSE    997 = Overlapped I/O operation is in progress.     0.0000063

    0000  40 b1 00 00 00 00 06 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 26 00 00 00
    0022  06 00 00 00 b1 fb 00 00 00 00


    The image buffer looks about right, since it is mostly a black image, refreshing it would give a similar result (camera senors are a good source of noise)

    0x00220024  is 0x22, IOCTL_ADAPT_SEND_NON_EP0_TRANSFER. buffered, any file

    This IOCTL command is used to request Bulk, Interrupt or Isochronous data transfers across corresponding USB device endpoints.

    0x00220020 is IOCTL_ADAPT_SEND_EP0_CONTROL_TRANSFER. buffered, any file

    This command sends a control request to the default Control endpoint, endpoint zero.

    So all making sense so far, back to the driver.

    Decoding the above for IOCTL_ADAPT_SEND_EP0_CONTROL_TRANSFER

    union {
           struct {
           UCHAR Recipient:5;
           UCHAR Type:2;
           UCHAR Direction:1;
           } bmRequest;
           UCHAR bmReq;


    bmRequest.Recipient = 0; // Device

    bmRequest.Type = 2; // Vendor

    bmRequest.Direction = 1; // IN command (from Device to Host)

    int iXmitBufSize = sizeof(SINGLE_TRANSFER) + bufLen; // The size of the two-part  structure

    UCHAR *pXmitBuf = new UCHAR[iXmitBufSize]; // Allocate the memory

    ZeroMemory(pXmitBuf, iXmitBufSize);


    pTransfer->SetupPacket.bmRequest = bmReq;

    pTransfer->SetupPacket.bRequest = ReqCode;

    pTransfer->SetupPacket.wValue = Value;

    pTransfer->SetupPacket.wIndex = Index;

    pTransfer->SetupPacket.wLength = bufLen;

    pTransfer->SetupPacket.ulTimeOut = TimeOut / 1000;

    pTransfer->Reserved = 0;

    pTransfer->ucEndpointAddress = 0x00; // Control pipe

    pTransfer->IsoPacketLength = 0;

    pTransfer->BufferOffset = sizeof (SINGLE_TRANSFER);

    pTransfer->BufferLength = bufLen;


    PUCHAR CCyBulkEndPoint::BeginDataXfer(PCHAR buf, LONG bufLen, OVERLAPPED *ov)


    if (hDevice == INVALID_HANDLE_VALUE) return NULL;

    int iXmitBufSize = sizeof (SINGLE_TRANSFER) + bufLen;

    PUCHAR pXmitBuf = new UCHAR[iXmitBufSize];

    ZeroMemory(pXmitBuf, iXmitBufSize);


    pTransfer->Reserved = 0;

    pTransfer->ucEndpointAddress = Address;

    pTransfer->IsoPacketLength = 0;

    pTransfer->BufferOffset = sizeof (SINGLE_TRANSFER);

    pTransfer->BufferLength = bufLen;

    // Copy buf  into pXmitBuf

    UCHAR *ptr = (PUCHAR) pTransfer + pTransfer->BufferOffset;

    memcpy(ptr, buf, bufLen);

    DWORD dwReturnBytes;

    DeviceIoControl(hDevice, IOCTL_ADAPT_SEND_NON_EP0_TRANSFER,
                      pXmitBuf, iXmitBufSize,
                      pXmitBuf, iXmitBufSize,
                      &dwReturnBytes, ov);

    return  pXmitBuf;


    So for the image transfer it looks like it is using


    So we can just follow the call chain CyApi and follow it up to

    bool CCyControlEndPoint::Write(PUCHAR buf, LONG &bufLen)

    Backtracking a tad

    On a personal note I have used IDA for a while but sometimes will forget to use it with all the tools it has and just manually convert all the offsets. But there are useful tools like FLIRT and people have tried to put together libraries of FLIRT databases, but there are a lot of libraries to do that for.

    Luckily for us Cypress publishes the PDB file for CyUsb3.sys so if you load it into IDA it’ll create all the structures that are in the PDB for you. After loading it and letting IDA figure itself all out, dump the typeinfo to an IDC, then reload the app(NeodenCamera.dll) that uses the CyUSB3.sys which is likely different and execute the IDC you just made, that will transfer the structs to the new RE project but not the offsets etc will will be wrong. Now keep a copy of the IDC somewhere you’ll forget all about for next time

    As an example the _SINGLE_TRANSFER structure which has a union (also mapped)

    00000000 _SINGLE_TRANSFER struc ; (sizeof=0x26, align=0x2, mappedto_45)

    00000000 ___u0           $902C784530A83C47F9612DF5432F758B ?

    0000000C reserved        db ?

    0000000D ucEndpointAddress db ?

    0000000E NtStatus        dd ?

    00000012 UsbdStatus      dd ?

    00000016 IsoPacketOffset dd ?

    0000001A IsoPacketLength dd ?

    0000001E BufferOffset    dd ?

    00000022 BufferLength    dd ?

    00000026 _SINGLE_TRANSFER ends

    Typing  that in manually the first time, after forgetting the union… then redoing it, then thought there must be a better way, and of course there is, and this is one of those ways, but no doubt next time it’ll just get brute forced again

    Now apply that struct to the code

    SINGLE_TRANSFER *__thiscall CCyIsocEndPoint__BeginBufferedXfer(int this, void *inputBuffer, size_t bytesToWrite, LPOVERLAPPED lpOverlapped)

          int v4; // ebx
          int blocks; // esi
          unsigned int v7; // edi
          _SINGLE_TRANSFER *pTransfer; // esi
          DWORD dwReturnBytes; // [esp+Ch] [ebp-8h]
          DWORD iXmitBufSize; // [esp+10h] [ebp-4h]

      v4 = this;
          if ( *(_DWORD *)(this + 4) == -1 )            // HDEVICE
            return 0;
          blocks = (signed int)bytesToWrite / *(unsigned __int16 *)(this + 12);
          if ( (signed int)bytesToWrite % *(unsigned __int16 *)(this + 12) )
          if ( !blocks )
            return 0;
          v7 = 8 * blocks;
          iXmitBufSize = 8 * blocks + bytesToWrite + 38;
          pTransfer = (_SINGLE_TRANSFER *)operator new(iXmitBufSize);
          memset(pTransfer, 0, iXmitBufSize);
          pTransfer->reserved = 0;
          pTransfer->ucEndpointAddress = *(_BYTE *)(v4 + 10);
          pTransfer->IsoPacketLength = v7;
          v7 += 38;
          pTransfer->BufferOffset = v7;
          pTransfer->IsoPacketOffset = 38;              // SINGLE_TRANSFER
          pTransfer->BufferLength = bytesToWrite;
          memcpy(&pTransfer->SetupPacket.bmReqType._bf0 + v7, inputBuffer, bytesToWrite);
          dwReturnBytes = 0;
            *(HANDLE *)(v4 + 4),
            lpOverlapped);                              // IOCTL_ADAPT_SEND_NON_EP0_TRANSFER
          *(_DWORD *)(v4 + 32) = GetLastError();
          return pTransfer;


    The Cypress SDK contains the source for that function, let us see how it compares

    PUCHAR CCyUSBEndPoint::BeginBufferedXfer ( PUCHAR buf, LONG bufLen, OVERLAPPED *ov )

            if ( hDevice == INVALID_HANDLE_VALUE ) { return NULL; }

        int iXmitBufSize = sizeof ( SINGLE_TRANSFER ) + bufLen;
            PUCHAR pXmitBuf = new UCHAR[iXmitBufSize];
            ZeroMemory ( pXmitBuf, iXmitBufSize );

        PSINGLE_TRANSFER pTransfer = ( PSINGLE_TRANSFER ) pXmitBuf;
            pTransfer->ucEndpointAddress = Address;
            pTransfer->IsoPacketLength = 0;
            pTransfer->BufferOffset = sizeof ( SINGLE_TRANSFER );
            pTransfer->BufferLength = bufLen;

        // Copy buf into pXmitBuf
            UCHAR *ptr = ( PUCHAR ) pTransfer + pTransfer->BufferOffset;
            memcpy ( ptr, buf, bufLen );

        DWORD dwReturnBytes;

        DeviceIoControl ( hDevice,
                              ov );

        UsbdStatus = pTransfer->UsbdStatus;
            NtStatus   = pTransfer->NtStatus;

        LastError = GetLastError();
            return pXmitBuf;


    The compiler has optimised some things away and made some little tricks but it is close.


    Reading an I2C EEPROM

    It’s definitely been Cypress week around here, with the RGB Blinky Ball (which has a cypress psoc4) we wante d to ship a programmer, the Cypress one is $90 so not that. A bit banging version for the FX2LP ecists so we hooked that one up and it worked great. There is a a fork here PSOC

    These boards are dirt cheap and are great for either reading logic at high speed or writing it.


    It also comes with an AT24C128 eeprom for boot strapping the FX2LP.


    Recalling that the Microchip PICCKIT can read EEPROMs with, and just happen to have one the desk for the One Key Keyboard.

    It needs this software.

    The I2C Address of the chip is set as

    A0 = 1 A1 = 0 A2 = 0 = 0xA3


    Next you have to mod the PICKIT

    > 24LC I2C bus devices:         Bus Speed-                 400kHz with Tools -> Fast Programming checked                 100kHz with Tools -> Fast Programming unchecked

            NOTE: Bus pullups are required for all
                  programming operations.  400kHz requires
                  2k Ohm pullups.

            NOTE: The I2C (24LC) Serial EEPROM devices require the following PICkit 3
                  hardware changes to work properly:

                  Remove TR3 from the PICkit 3.
                  Remove R50 from the PICkit 3.

            Connections for 24LC devices
            PICkit 3 Pin             24LC Device Pin (DIP)
            (2) Vdd                  8 Vcc
            (3) GND                  4 Vss
            (5) PGC                  6 SCL (driven as push-pull)
            (6) PGM(LVP)             5 SDA (requires pullup)
                                     7 WP - disabled (GND)
                                     1, 2, 3 Ax pins
                                        Connect to Vdd or GND per
                                        datasheet and to set address


    Load this “OS” into it



    Hook up power, ground , sda and scl ,  Make sure before you add power you start a capture, since it only sends and clocks when transmitting you can trigger and capture or just run a few second capture that’ll give you time to switch on the power.

    The PicKit 3 is meant to be modded , trying it first. remove the R50 4.7K, and the 5.0V Zener at TR3. The software continually resets the 3.3V to 5V so watch for that.  This is a clone of the Pickit 3 the lower right MELF is the diode TR3


    This didn’t seem to work very well and the data just reapeated, and was very sparse i’ve seen enough code in binary to know its not right. Figured it was worth a shot.

    So next to try is the Ginkgo box which is a USB to CAN/I2C/SPI/GPIO test box it is useful for all sorts of things and originally it was used to do automated testing on some SPI control systems. But also a no go I suspect it was the CAN firmware but the doc’s are less than clear about it (it was. you can reflash any firmware on to it and even though the label says I2C/SPI/CAN it has internal checks note: add to list  of next projects also there is an internal jumper if you set it you can recover the bad flash)

    So lets just use a Logic Analyser, coincedentally we made a mini logic analyser that was based on the same 68013A chip at NSL called the AnnaLogic which is plenty capable to do this, but lets use\ the Logic Pro 16 instead.


    It pops out immediatately and now it is easy to recognise the fimrmware since all the fx2lp bin’s I’ve seen to date have had a 222222 sequence before the last few bytes., next is to convert the trace from the Saleae to a bin and load it into my FX2 dev board. The text file export has the time stap and address, followed by the data byte, just extract that column after removing the setup code.

    Time [s],Packet ID,Address,Data,Read/Write,ACK/NAK

    C2 is the start of the EEPROM

    The logs show that after a tiny pause from the firmware download to the FX2LP it starts sending out on I2C address 0xBA


    time gap


    Noting that is likely a camera on the I2C addres 0xBA, so searching for I2C Camera 0xBA and the first hit is the MT9M001

    Array Format (5:4): 1,280H x 1,024V
    Monochrome sensor
    Slave address 0xBA (SADDR=0)

    It is pretty close to what we expected.  This camera is very popular for interfacing to the FX2LP. There are apparently different versions of the Neoden4 with a different camera, and that vendor uses similar sensors too. But we don’t really need the model yet.

    On that MT9M001 sensor 0x8C is a reserved address, so not sure if its a good match

    Next task on the list is to sniff the CAN busses, and because of the previous automotive woirk NSLLabs posses every CAN adapter known to humanity, so that should be straightforward..

    …more later…

    • nope 7:33 pm on March 7, 2019 Permalink | Reply

      password to change language
      macInt = [int(t,16) for t in mac.split(“:”)]
      for i in range(3):
      t = macInt[i*2] + macInt[i*2+1]
      code=”neoden2015 {0} Language”.format(code)
      password=binascii.crc32(code.encode(‘utf-8’))% (1<<32)

    • charliex 9:21 pm on March 7, 2019 Permalink | Reply

      Nice thanks

  • charliex 2:15 am on December 13, 2011 Permalink | Reply
    Tags: , pick and place, pickobear   

    [Null Space Labs] = Juki 360 Rebuild, Part IV. Yes we’re in LA. 

    So it been a while since my last update, my lil’ dog of 16 years had to be put down and I wasn’t feeling it, then family visited from so we stalled abit.

    So quick updates for the tl;dr people :-

    • Head rotation works, but have to add an encoder wheel since we ‘forgot’ about the tool home position inside the head tool pickup…
    • Y axis is all upgraded, new stepper motor drivers are in 1/2 step is on.
    • Switched to GRBL and StepDir, instead of, uhh, not stepdir.. Though using the
    • OpenPNP base firmware revision, i didn’t realise it was different til after porting it.
    • GUI is almost done, it can place one full board from feeders and trays
    • Replaced hoses as the old ones burst, which only happens when you say right lets video this.
    • Trapezoidal for the servos courtesy of Grbl.
    • Still having some issues with the vacuum sensor, might be bad or low pressure or something else, turned it off for the moment.

    Pickobear uncut.

    So we’re able to pick up and place with the new Grbl firmware and the updated GUI, its all GCODE now. I’ve moved more of the functions into the GCODE vs individual move, drop , vacuum, since the communication is just serial rx/tx and serial is a PITA for this sort of thing, since its all ACK/re-ACK. With an M command its just e.g. M21 pickup part, M22 putdown part and the machine reports yes/no. FTDI’s are decent chips but they will break down comms after long datastreams, I’ve had a lot of problem with them and high speed streaming, as well as overlapped IO support is meh, events don’t work that well so multithreaded serial comms based on events is in theory great, but the practise doesn’t always work well. so its all software handshakery.

    We’ve done a lot of changes since the last update.


    Grbl’s great, its easy to use and easy to add on too, we were already using the AtMega2560 anyway so lots of space, even with all the stuff I’ve added to it, we’re at maybe 17%of the  flash space. I had looked at it when we first started out, but it needed StepDIR and were using – + , our original drivers didn’t support StepDIR. Even luckier I’d put all the stepper controls on the same ports.

    Changes for the 2560 are minor, just an interrupt rename and i don’t the sleep mode is working as it ought too, i assumed it was and then wondered how to handle the panel switches but turned out when i added the head movement ACK that it was, I was going to use timer3 to handle switches but then i noticed the main loop was executing constantly, some of the AVR’s have different sleep rules.

    One thing i did do though is since the mega ICSP is under the shield and its on the machine, which is 3 metres away from desktop. I left the Arduino serial boot loader  on the chip, this way i can just script AVR Dude to load the firmware built in AVR Studio 4 to the mega over serial. so its a lot easier, the downside is that if you press a key too soon the boot loader seems to kick in, i haven’t checked this to be the reason for sure, but it seems like its that. Not a huge deal.

    Running a test of the GUI

    Pick and placing, it takes a lot to just do this seemingly simple thing.


    Head rotation

    Head motor, luckily we used a double ended shaft motor in case we needed an encoder, which it turns out we do. Look closely at the top of that shaft, see some thread? yeah we didn’t…. We CAD’d up a mount and fitted the motor.

    The motor is 1,000 steps for a full 360 degree rotation, should be able to place the craziest of our boards, we like some gangsta lean in our part placements.

    As a side note the magnetic sensor on top of the head, really don’t like the way they’re made or mounted, that wire takes a beating and it’ll fail again so we have to fix that too. You can see all our temporary fixes for the hoses that keep cracking, they’ve lasted 20 years though. The red line is the new one we’re using, which is way more high tech than what was available in those days, it should last longer than the tech is needed.

    The rotation head needed a larger power supply, so that is now installed. Its the 24V power supply previously mentioned from all electronics.

    Since we made the shield with the extended pins its easy to add things on to it. And now we’re on StepDir its just one wire to control the head ( Though we make a semi-fatal error at this step in choosing which direction the motor runs )

    Setting up a test

    After the rotation head was added to the software, i test placed two parts, they’re off, at this point i think its the solder paste or tape missing, so we add some. As you can see though the parts are rotated as they should be. We were uStreaming with the second camera so its all hanging off in space at the moment.


    Krs adds some paste to the test pads. She’s the one who wants this thing finished the most as she builds most of our boards Smile

    I’m remotely moving the head with the camera view. First home, then moving to the PCB to register the location of the part, then going through the steps of the part placement.

    So the mistake in the choice of direction for the head rotation is that the head can unscrew itself and drop down the tool, the sensor doesn’t know the head is down and the tool is now below the PCB holder and auto changer, so the head will gleefully zip around and bang it into whatever it can find, which bends the head, which is not good at all.  So we’ll reverse the direction of the motor and use lots of Loctite. This is why the test videos show the parts poorly placed.

    Pity these lessons learnt cost us a head each time.. But we move on..

    A trial run through all the parts ( using 1206 resistors since they’re like .00004 each) as you can see they placed perfectly.

    Ok, maybe not what you’d call perfect but they did place in the right spots, the head down motion was a little too strong and there’s no paste to hold the parts in place so they’ll wander around every time a part is placed. Still the software is working.

    The vacuum sensor needs to be set quite accurately or it’ll do things like this :-

    The tool is sitting perfectly on the vacuum test pad, the head brought it down to see if was there, blocks the air coming through the tool, but since it wasn’t picked up correctly ( head wrong orientation ) it just left it there.

    The head moves to the pad, goes down, the air comes on, there is a settle time, then the machine reads the vacuum sensor, if its not blocked there is no tool, if it is, then there is a tool.  That is how it decides if the tool needs to be picked up or put back.


    Finally we place a board with paste and all !

    So last night(this morning) we finally managed to run a real board. For some reason i decided to go through each of the components and rotate them 90 deg rather than change the rotation in the feeder…. derp… As we did this the vacuum test failed, so you can see me going in change the code ( incorrectly!) and re running, it the bit you might not see is the machine doing a loop of picking up a part… i fix that and we move on.. First run we do a step by step. I noticed a few bugs as we go along so there are some pauses as we fix those.

    Stencil pasted a board, you can read about this in our Arduino Mega build blog entry. Its a CNC’d brass sheet.

    I taped the board down to stop it moving around, probably not needed.

    Some of our feeders need some love and i wasn’t super careful about where i was picking up the parts from so it was a little sloppy, however it was 2AM and i ended up having to leave and come back after one of the alarms at work went off Smile

    MMCA used one of his awesome swiss files to ‘fix’ one of the feeders that was catching

    Here are some more GUI videos, they’re probably only interesting to those of thats like watching these things over and over..



    Annotated some of the steps in the GUI.


    One of the feeder pickups i didn’t setup properly, so it was placing them off!

    Onto the hotplate, i wanted to see just how bad we could place parts and if they’d fix on the plate, all but one pulled onto the pads, the other worked but was right on the edge of a pad, it’d work fine. All we have to do is take more care when tell it where to pickup

    Krs also has a bunch of videos of the machine placing that first board.



    Anyway that is mostly it, we’re down to the tuning and bug fix stage so its been pretty fun.

    But if you’ve got minerals help out the OpenPNP guys they’re doing good work, our stuff is available to everyone, but its reasonably specific to the Juki though it is GCODE.


    Small update: Last night, I started adding the second USB port of the ADK to Grbl as well, its almost finished. I based it on the usb.cpp from ADK but i converted it to C  since the c++ generated a larger firmware and there is only one USB port, I want to keep the firmware tight.

    BTW If you’re reading this and are on the yahoo zevatech list the moderator who’s apparently building a commercial retrofit for this machine is blocking our posts sharing this open source project. He said he wants credit for a CPU suggestion neither his or our project uses, a fix that wasn’t broken, and a camera solution we didn’t ask for advice on, or use.



    Miss you girl

    • phil 11:37 pm on January 30, 2012 Permalink | Reply

      Hello. See your work on the Juki 360. My 360 just stopped working because of scratched 5 1/4 floppy. No backup. Can I buy a a copy of a good system disc from you? thks, Phil

    • phil 5:30 pm on February 1, 2012 Permalink | Reply

      Hello again. How much and where do I send $ to? Phil

    • charliex 3:15 am on February 2, 2012 Permalink | Reply

      the floppy is available on here , you can download it and make a floppy of it.


    • phil 5:17 pm on February 3, 2012 Permalink | Reply

      Thks. We’ll be attempting to make a floppy from it soon. Thks again! Phil

    • phil 3:36 pm on March 5, 2012 Permalink | Reply

      After weeks of trying to get an old pc ( which has a 5 inch floppy drive) going, we’ve come up short. Unable to download your file. Any possibility of making a floppy at your end and sending it to us?

    • phil 3:32 pm on March 8, 2012 Permalink | Reply

      Sent you an email with contact info. Thks, Phil

      • charliex 9:42 pm on March 12, 2012 Permalink | Reply

        havent seen an email phil

    • phil 7:05 pm on March 19, 2012 Permalink | Reply

      Sorry. What is your email? I sent it to: –yep thats it — charlie!. Thks, Phil

    • charliex 7:16 pm on March 19, 2012 Permalink | Reply

      i’ll check my junk mail folder

    • phil 1:16 pm on April 12, 2012 Permalink | Reply

      I sent email with info just now. thks, phil

  • charliex 5:59 pm on September 15, 2011 Permalink | Reply
    Tags: arduino, , machine vision, open source, pick and place, zevatech placemat 360. juki 360   

    Juki 360 rebuild PART 3 ( The not a repeat edition) 

    So we’re on stage III of the rebuild at Null Space Labs (http://032.la) . What we’re aiming for is :-

    • Rotation servo in the head
    • Consistent speeds
    • Addition of more checks

    Adding more checks was straightforward, added code to determine if the machine picked up a part, with the small compressor we were using it’d start to loose vacuum so if it failed to pick up a part, the delay became longer. This  allowed the the compressor to catch up a little, as well as alert the user that something was amiss.  Next it checks to see if the part fell off during transport to the board. The machine has a vacuum sensor that knows if the top is being blocked. This lets the machine controller (Arduino) know a part is probably there.

    The machine won’t move if the head is down, and the limit switches were put on interrupts. tool changers are tested and put down if they’re up for some reason.

    The interrupt based limit switches are nice, anytime a change of the signal is recognised, either in limit or coming out of limit the routine is called and it reads the status of the pin again, and sets the out of home condition, so that machine controller knows the machine should be rehomed before continuing.  The code is simple and looks like this in the setup()

    attachInterrupt(5, x1Limit, CHANGE );

    void x1Limit( void )
      homed = false;
      xLimit1 = digitalRead( XL1 );

    attachInterrupt takes the pin number , the function to call and type of signal to look for, HIGH, LOW, and CHANGE, i use  CHANGE so that the xLimit1 variable is always set properly, and rather than rely on software tracking I read the state back in.

    I’ll probably change the digital read to actually just read the PORTx value directly, you want to minimise time spent in an ISR (interrupt service routine ) as much as possible. 5 is the pin that the XL1 limit switch is connected to on the shield, and homed/xLimit1 are volatile global variables that have the condition, they are set as volatile since they can be changed at unexpected times, either by the hardware directly or the ISR. volatile lets the compiler know not to cache the value or other trickery. This is often the case with things like memory mapped hardware clocks where you read a specific memory location to read a clock, so you’d do something like (clockVal would be set to the specific memory location by the linker or other directive)

    const volatile unsigned long clockVal;

    We’re not using const for the homed/xLimit1, since we are writing to it via software, a hardware clock changes outside of our code.


    Someone knocked one of the feeders slightly off (probably me) as it was being worked on, I didn’t notice and moved the head.

    It caught the feeder pin ( bottom left) and bent badly enough that it wouldn’t go back down. So i pulled it apart and used a vice with cut outs and gently squeezed it back into shape, then mmca used his micro lathe to make it straight again. Luckily these are easy to make from scratch.

    Servo Pulses

    The servo pulse speeds are more of a pain, the Arduino has some fairly inconsistent timing and some of the functions you’d expect to be fast, aren’t, the delayMicroseconds has a lot of skew, digitalWrite takes a long long time. Once you know about them, they’re easy to deal with i replaced them with bitSet/bitClear and the __builtin_avr_delay_cycles feature in avr-gcc.  We captured the output of the servos from the Juki PC with a scope and my logic analyser. That allowed us to see the ramp up and down times to get the head moving at a decent clip. There is still some ripple in the software PWM though, so we’ve converted most of it over to just  AVR asm/C and setting the ports individually,the Arduino does what it is meant to do really well and that is be easy to use, but what’s uncommon about it is that it easily lets you drop in code that bypasses or override the built in libraries, in my experience that’s rare in other simplified dev kit software, its typically all or nothing.

    Changing the digitalWrite is straightforward. Determine which port the pin you want to set is on and then use bitClear or bitSet instead.

    digitalWrite(YCCW,HIGH) becomes #define YCCW_HIGH   bitSet(PORTF,3); YCCW is defined  as A3, which on the mega is on PORTF, bit 3. digitalWrite LOW is bitClear(PORTF,3)  going even further the PORTF can be manipulated directly with PORTF |= (1<<3) to set it, then you can combine them together with PORTF |= (1<<3)|(1<<2) etc to clear PORTF &= ~( (1<<3) | (1<<2 ) ) also there is a _BV() macro that does the (1<<n) shift. so

    PORTF |= _BV( 3 )

    Using |= and &= should output the closest ASM instructions that you can could in C.

    Once we started to investigate the timing from the Arduino that is when I noticed the problems, even with interrupts disabled we were seeing inconsistent results. So I wrote some test code and hooked up the output to the LA and set about trying to make a micro second accurate delay. specs showed a 2uS rise , 5uS pulse and 2uS fall for the initial step pulse, the stepper IO controller is HIGH delay LOW delay HIGH . for the first delay about 8uS works well.

    We captured the data with the logic analyser and pulled the data into Excel which is great for this sort of thing.

    A quick couple of macros and copy/paste and we’ve got differences in Column F , you can see the length of the pulse in F3 and F4 has the off time length, which gives us our full duty cycle for that pulse.

    In H its just the lengths of the off times since the on time is constant. Column I has the differences.

    One of the next things excel can do is trend lines, I did this by hand and then arko showed it to me, I’d seen the equation solver before but not this.

    First create a line chart with the off times (the part of the duty cycle that’s after the pulse)

    Click on the data points in the line to select the line. then use the trendline option

    Set the options like so :-

    Which gives you this trend line (in black ) and the algorithm for it. I opted to use a table lookup since it has to be super fast for the Arduino.

    Time Travel

    Skipping back a little after the last blog post we did an 0603 repeatability test, used the layerOne speaker badge as a base, I exported the centroids from eagle for one side and ran test code. As well as just have the machine draw lines of 0603’s. with a logo-esq list of commands.

    The left side has a little wobble, partly pickup, partly the slightly bent head ( we’re lucky that a generous person sent us a new head and feeders you rock Steven ! ) the right side is a similar test but its placing the 0603s on top of each other, that actually worked pretty well a few of them fell over. But given the bent head, no centering or machine vision its working better than we can hand place them. This picture is the version that just places the parts in lines. I also think our stop is a little aggressive in this test, and this is before we really got into the ramping.

    The ramping is simply having the PWM have a longer off time, pulsing the servo, decreasing the off time, pulse it again and do that for N pulses until the motors are moving fast enough to get to the top speed, that’s what the Excel sheet is for. I’ll go into it in more detail in another post.

    Someone from Russian TV decided to film it, Central TV i believe. Unfortunately all the airlines are now tight so i can’t remotely shoot air or disconnect hoses at him anymore.

    Adding a camera

    This is a Cognex 4100 machine vision camera, machine vision cameras are generally low noise, stable image sources. This one can determine angles, and so on and then send them out via Ethernet or serial. The 4100 has been dropped by Cognex it seems and the software needed to run it is an old version, which has a lot of compatibility errors with Windows 7, its been something I’ve seen before where the menu’s lockup for a long time. But we figured our way around it by using Windows XP inside 7 with its builtin Virtual PC and got it to see the rotation.  Think RoboRealm built into a camera.

    Controller unit

    Inside the camera, TI DSP and a Xilinx FPGA, very nice.

    Camera head

    We’re also using some webcams , a couple of them are microscopes, one is from think geek and it’s a wireless HView camera, the other was an eBay special pen microscope usb2. Oddly finding a 12mm drill bit to make a hole for the camera wasn’t so easy.

    Adding the camera needs two pins for the servo CW/CCW, three pins for the camera, TX/RX and trigger. The shield has three pins left!, Though I did cheat and use two of the pins as a via since I didn’t’ really want to deal with the LPKF via rivets even though its only a few of them. We do really only need RX though.  The trigger just tells the camera to only operate while, or after the trigger is set. The final board will not use the cheats of course, but before I remill a new one we have to

    Software testing

    This is our test bed software Pickobear it allows us to test OpenCV (though we have a Cognex camera), the eagle script output CSV is imported into Pickobear and then the machine knows how to place it.


    One of the problems with test software is you often leave it in a state where it was testing something specific, and you forgot to remove it , case in point, I had one extra YCW pulse that caused a problem when we were doing a tool change test. After 4 changes it’d refuse to put down the changer, we checked air pressure, offsets, tool head distance and all that, eventually did test code that moved home, then to 0,0, 1000,1000, back to 0,0 and saw it was one Y off each time. A quick check of the firmware and the extra pulse was spotted. But it did allow us to get the numbers needed to exactly place the adjustment for the pickup head.

    Next we’re testing out different cameras, I milled out a quick holder from PCB material and the epic blue painters tape. This is a nice’ish USB 2.0 camera that’s setup as a microscope, it came with a nice metal holder and was about $80 from eBay. M4 nuts and bolts seem to be rare around us, never mind the M2.5’s we were looking for locally last week. So zip tie to the rescue temporarily, it does allow a little yaw though but it is ok for testing the software until the bracket arrives.

    To make this I measured the hole sizes, the distance apart and picked a distance for the camera, then guessed the size. I then added two drill holes with the hole command, set the size to 4mm for the larger 12mm hole I used the milling layer, draw a circle with the circle command, and set the width (right click circle and choose properties or type change width ) to a very small number, if you use 0.0 as the width eagle will fill in the whole circle and doesn’t cut anything, a width of 0.0 works for milling wires, but not circles. Then milled it out on our AccurateCNC 560H.

    Now we fit the new holder, has a nice set screw to hold the camera in place.

    Next is adding camera movement and offsetting the board. I did this by adding a mouse click even to the custom control, it then determines the center of the image and a difference calculation, then depending on the direction it needs to go it moves that amount, I still have to calibrate what the relationship of the camera image is to actual axis steps, but its close enough to use right now.

    I’m using videoInput with OpenCV and OpenGL to display the data from the cameras, the crosshairs and circles are drawn into the OpenCV image using their primitives.

    cvLine(img1,  xyF, xyT, CV_RGB(0,0,200),2);

    Camera setup is straightforward

    int numDevices = VI.listDevices();

    m_camera = DeviceID;


    img1 = cvCreateImage(cvSize(VI.getWidth(m_camera),VI.getHeight(m_camera) ),IPL_DEPTH_8U,3);

    img2 = cvCreateImage(cvSize(VI.getWidth(m_camera),VI.getHeight(m_camera) ),IPL_DEPTH_8U,3);

    There is a nice simple class I’ve used for rendering OpenGL into an MFC custom control for a while.


    Head servo rotation

    The rotation head is the next thing we’ll get fully operational. We decided to upgrade the 24V power supply since the little servo can draw more amps than it could safely handle. All electronics has suitable 24V 6.5A deals.


    $26 at the time of writing.

    A custom CNC’d bracket was designed by mmca and laid out  in solidworks by arko, the motor, pulley etc are all added. This allows us to switch from the simple 90o rotation to full a 360o’s so we can place the part turn 360’s and moonwalk away from it. There is some play in the rotation shaft because of the set screw, so that is being remade so there is no backlash., even though its a tiny amount and the software can correct it. The machine is able to do more than 90o in steps of 90o by simply changing which side the feeder is mounted too, but we want 45o’s for those gangsta leans.

    The top bracket/reed switch isn’t mounted or screwed down, that is why its tilted.

    New standoffs added and rest of head mounted.

    So here is a short video showing the setting of the PCB offset, I home the machine, pick the part I want to use a registration. Then move to the location where it really is and press the OFFSET button., then I move around to other components just to check its working, again the blue lines are OpenCV doing stuff.


    Motor Drive Upgrade

    The Y motor driver and motor aren’t that great, so we changed it out for this controller

    First removing all the old wiring and converting it to the style connectors, it’s a shame to do this as the wiring is just so nice compared to modern equipment. Its not often you see this level of attention anymore.


    New controller in place of the old one, it fits pretty well its longer than the old one but luckily there was lots of room, we just have to extend the AC lines a little.

    Removing the plastic conduit covers and rerouting the wiring.


    Almost there, I’m heading out early tonight and we apparently don’t have crimpers at NSL so rather than botch it, I’ll pick up some tomorrow or bring mine in from home


    A new motor was fitted too.

    Dirty fans!


    Close up of the belts and cogs etc.


    New vexta motor


    Machine powered up and homed ok ! sweet..


    I’m not sure how detailed or long to make each of these posts, since from reading the comments and emails, its obvious a lot of people skim or don’t read the text and look at the pictures, but I want to make sure we give enough details ( plus the wiki/svn ) so that people can recreate it. We are at the point we can pick and place our boards, surpassing the original software in some ways, a few things are left to be done to make it 100% of the original features sets, mostly to do with feeders. But we’re waaay beyond what it could do in some ways.

    • DAniel 3:27 pm on August 17, 2015 Permalink | Reply

      Hello Charlie, I had looked a lot to your site. I would like to retrofit a PM460 as well. I have bought 2 2 pieces of RKD514L-C in order to have step & dir input and microstepping.
      Do you know (if yes please tell me) :

      reduction raport on x and y axis ?

      motors : – what coil current do they need for each axis ?
      – what step angle do they have ?

      I say that you have changed the motor on Y axis. Could you please tell me what was wrote on old motor ?
      Any details would be very valuable for me.
      Kind regards,

      • charliex 6:41 pm on August 17, 2015 Permalink | Reply

        Hey Daniel,

        Honestly i don’t recall those details, that project is about 200 projects away in time. Everything remembered would be me just reading up on the blog postings. we pretty much changed over motors and drivers to whatever we had laying around at the time

  • charliex 6:29 pm on August 31, 2011 Permalink | Reply
    Tags: , 460, , pick and place, zevatech   

    Juki 360 rebuild at [Null Space Labs] 


    This is log of the current work we’re doing at NSL http://032.la

    Rather than hand build all the badges for our  socal security conference layerOne again, http://www.layerone.org/ we’ve gone to a pick and place machine.

    Gleep found us a Juki(Zevatech) Placemat 360 (that seems to  have been upgraded to a 460 ) pick and place machine. It was sold as ‘working’, the sellers definition was, if I’m completely honest a stretch (outright lie).

    This is actually our second pick and place machine, we don’t mention the other one Smile

    We’re also interested in acquiring a Zevatech/Juki 460 if you have one for a decent price.

    Basically he demo’d everything that didn’t need a compressor, that all worked. Of course everything that needed a compressor as we found out later, didn’t work! Still $1,200 isn’t bad.

    I used my supersilent 20a as a temporary compressor, it only has a small  < 1 gallon tank, but its actually quiet, we used the 8 gallon compressor at null space which is deafening, so i found a 3 gallon temporary one at harbour freight for cheap in their recent sale. its too small though, so we’ll need shop air at some point. The supersilent was causing the pickup head to fail to work after a few passes, so this caused as a few false starts, the machine needs a solid air supply to function , even in testing.


    The existing filter and pressure regulator was a mess, so off to home depot to come back with the best we could find there, which isn’t that great.


    This is the old one, remember sold as working. No filter, and all these bits were just lying around inside it.

    The machine itself is based on the PC-8801 Z80 4mhz CP/M which I recognised straight away as my old job had me doing game conversions in Japan for the PC-9801.



    The whole machine works pretty much on the principal of that if the CNC software said do this, do that, that it executed perfectly. Only limit, head, home and the tool changer have checks.

    We fired it up , Krs and Gleep got it picking and placing a few resistors (though they somehow managed to get the tape removal part completely wrong and it was throwing resistors all over the place. Then mmca got it placing QFP parts correctly. The lamp spot system was off, the 90’o rotation was off, the tubes were old and cracking. Compressor filter was non existent and rusted out. We’ve also discovered the whole thing is covered in parts from the previous owners, we’ve scored a few 100 0805s and some IC’s.


    Free Parts!

    The reed sensor was the first thing we found that was broken, a quick trip to eBay and a few days later we had replacements. Luckily Juki is in heavy use, and they use a lot of off the shelf components. Apparently the later 5xx machines do switch to a proprietary drive system.


    The reed switch detects if the head is up or down. Its one of the few sensors in this machine. The bend has caused the wiring to break down internally over the years. so the machine gets confused about being up or down, and the software doesn’t cope well with that, it basically needs a full reset afterwards.


    The new sensors , $9 from eBay.

    I also bought a CPLD based floppy emulator from Poland, it hasn’t arrived yet and we’ll probably be done with the new system before it gets here, and we’ve discovered the speed stays the same but floppy drives won’t last so the SD is still a good replacement.

    Placing QFPS (AT90CAN128)

    Fashioned a quick tray for the IC placements. We use these great little boxes, also from eBay, for holding SMD components, they double up as handy platforms too.



    The feeder is controlled by the head, it moves over the spring loaded pin and pushes it down, this releases air and the notched wheel on the right moves the component reel tape one step, at the same time the protective covering tape is peeled away, allowing the machine to come back and pick the part up. This time, they’re correctly threaded, previously the protective tape was wrapped around the pin in the middle.

    Side view of feeders, you can see the reel of components on the left, and the pneumatics underneath. Its important to choose a pick and place with a widely available  range of cheap feeders, all too often people buy a cheap pick and place then find out it has none, and it’ll cost $1000’s to get them, if at all.

    Feeder with pneumatic assembly

    The expansion board

    This is the board inside the machine, it is a couple of 8255s which are the defacto standard for PC parallel IO, almost every PC has had one or more of these, they’ve since moved into the ASIC’s but the principle is the same. It memory maps each of the input/outputs of the machine so that host PC can see them. I pulled off the floppy image, copied the files to my PC and reverse engineered the controller code with IDA.


    I found an IMG of the floppy online, this was MFM encoded . So i converted that to a raw binary file, and then used cpmtools to copy the files from it. I was hoping to find some of the saved files so we could reverse the format and write a quick tool to do the placement. Once the files were copied off i tried a few of the different PC-8801 emulators, M88 etc, but had no joy in getting it running. So finally I just pulled apart the CP/M COM files in IDA and see what we could find.

    The teaching process is tedious, so reversing the format would have been worthwhile.

    Interface board

    This board takes the IO from the PC, buffers 74LS240 it and uses power darlingtons FT5723M to switch the 24V signals for the pneumatics.  As well as read the various sensors and the + / – for the motors. The motors and stepper drivers are off the shelf, but very nice, we even have newer versions of the motors and controllers at NSL.I’ve removed the bottom connector to make it easier to take pictures.

    The grey cable that has been added later is the automatic tool changer, this is soldered directly in the spare connections , 5V and 24V VDC. The 5V powers the small adapter board in the ATC and the 24V is for the pneumatic switches.

    The remaining signals are multiplexed IO that are demuxed by a 74Ls138 on the ATC board, which deviates from the way the rest of the board works as the rest are all controlled by the darlingtons directly.

    Each function of the machine is basically <control> – <buffer> — <pc> – <memory map>

    So if you want the head to go down, you flip a bit in the PC’s memory. Its all digital IO, nothing fancy at all. The only extra part is the 5V TTL to 24VDC for the pneumatic switches.

    Stepper drivers and power supplies.

    The stepper drivers are on the bottom, the other one is to the right under the tray. the two power supplies are just visible at the top right, one is a 5V the other a 24V. The power filter is in the lower left.

    Power supply

    Stepper motor driver

    XY gantry

    Since the machine was in bad need of service, we stripped it down, here the XY belts are visible. The top side has the the driver motor and the bottom side gets its power from a rod under the bed on the right side, so both belts are moved in unison. The ATC is in the top right and the frame in the middle is what is left of the PCB holder.


    Tearing it down.

    The head

    mmca stripped the head down. here it is removed from the gantry. mainly because there is a piece of string visible , and we can’t figure out what its for.


    Shims, we don’t think these are factory shims.

    The strange piece of string inside the head… What could it be for?

    Bottom view of the tool pickup and the 90’o rotation.


    These 4 arms are moved towards the part and clamp it gently, this straightens the part for placement, it can also rotate the part by 90’o ( which sucks for us because i always like to put parts at 45’o)

    The laser, focused lamp (this machine continues to surprise us ) which is used to position the head in teaching mode.

    We’re removing the lamp and replacing it with machine vision, so some measurements are taken.


    The hoses are removed and marked with a letter , the corresponding connector is also marked with the same letter.

    This is how the previous owners repaired the 90’o rotation arms….. so that explains the string. this was removed and repaired correctly. The 90’o does just that, it rotates a part by 90’o that’s all this machine can do, so we’re going to change that to it can do arbitrary rotations.


    This hose had cracked, a few others did too. I found a few temporary replacements at the auto parts store 4mm ID, 8mm OD  fuel priming line.  The plan is to replace all the hose.

    Stripped machine screw in the head. Replace from grainger, M3x8mm 0.4mm thread 5.5mm head size.

    And some missing set screws

    Spent some time measuring all the screws and what not. The machine is old enough that it came from proper manuals with circuit diagrams.

    We’re replacing the IO board, the plan is to throw in a TI Stellaris ARM lm3s9b96 chip instead, (TI were good enough to send us a bunch a while ago, thanks TI!)

    This board is a dumb board, it just marshals the I/O and does the switching of the 24VDC with darlington’s.

    Here we’re removing and verifying the connector sizes and function  (the manual had some errors) so its good to do that. It also gives us good insight into what’s going on.

    Checking how the machine works with my trusty fluke.


    I threw together the connector layout in eagle and printed it out to verify it,  early revision.

    Measure the hole size and distance. Our board is exactly the same size so its a drop in replacement, we’ll just lose the two larger connectors and change it to USB.

    Here we were figuring out how the ATC worked, at first it was though to drive it directly , but there weren’t enough wires. So its 24V, 5V and control signals, the small interface board at the front is a  74LS138 decoder/demultiplexer with a few buffers and more darlington drivers , it switches the 24V on and off based on the 4 control signals coming in.

    Automatic Tool Changer

    The tool wanted is lifted up when the machine wants to change it, on the right are the pneumatic switches that are controlled by a 24V signal.



    We’re using Power MOSFETs to control the 24V instead, a 6 pin ROHM US6K1DKR in a TUMT6 package ( time to create a new device in eagle again !) I ordered 100 from Digikey yesterday and should have the board layout finished today. Then we can mill out a test PCB and see how it works. (parts arrived a few minutes ago!)

    You might be amazed, I was , about just how simple this machine is, you could run the whole thing from a set of on/off switches, albeit very slowly. But that is great for us though as it makes it very easy to replace the PC software.

    The next big thing is going to be testing the new power MOSFET and building the new PCB.  The chips will be here today have arrived.

    So new eagle package

    Cut out a few to test.



    Apparently I goofed on the measurements, I did change it around a tad after the first revision. Teeny part.

    Soldered it anyway


    So the next step is adding cameras etc.


    mmca explaining the new part to be cnc’d out for the camera


    mockup of the mount




    The head has to be recalibrated so the bottom of the tool is 62.5mm from the table, with a .1mm accuracy, so we as usual went overboard and used grade B gauge blocks.

    69.5mm to .00005 inches accurate.


    Gauge blocks are fascinating, they stick together like magnets if you put them together by making surer there is no air between them, but if you just stick them together they won’t. Super flat. these aren’t grade a or better, but they’re nice. mmca has the coolest stuff.


    Starting to rebuild it



    Machine vision tests

    This is work in progress, testing RoboRealm/OpenCV and teaching it components, it works well!


    Using a panda board a HP HD Webcam for testing the vision.



    Playing around with layouts for a quick test tool. two grey areas are for the cameras.


    Well that is it so far, my Motorola Atrix decided that the fingerprint reader would become burning hot to the touch. So I pulled it apart and removed it, but somehow managed to make it do a full hard reset (or a docwho76 as we call it ) and it deleted a bunch of my pictures. google+ had failed to sync them. But we’ll keep documenting the project,

    • Jack Gassett 4:52 pm on September 1, 2011 Permalink | Reply

      Hey, this is great guys! I have the exact same pick and place unit and went through the same pains getting it up and running about a year ago.

      Thought I’d share a couple tips that I learned, you might already know them, or they might help. 🙂

      I was having trouble with the autochanger detecting if a bit was attached. At first I thought it was the reed switch. But it turned out that it was actually the PS4 pressure switch. You can put it into test mode, drop a bit onto the rubber pad by the autochanger and then look for a red LED to light up on the PS4. Adjust the screw on the PS4 to get it just right.

      The other tip is that I don’t even bother with the teaching light anymore. Screaming Circuits has an EAGLE ulp file that generates a centroid file with exact coordinates of your parts. It saves a LOT of time to just print out the centroid file and type the exact coordinates in instead of teaching each component location.

      Hope this helps, and hope you guys have as much fun with your Juki as I have. 🙂


      • charliex 5:06 pm on September 1, 2011 Permalink | Reply

        Hey Jack,

        Yeah i saw you on the zevatech list, seems like its a small world!. We’re completely replacing the PCB and rewriting the software for it. The reed switch was definitely broken, and once repaired and realigned the head, it picks up stuff really nicely now.

        The initial plan was to reverse the save file and put the centroids directly into that, but we decided to go whole hog and just redo all the software.


        • tom 1:24 am on May 12, 2015 Permalink

          Hi Charlie,

          I just got a 460 machine for my small business and would like to know if this project was completed? Is the software and hardware schematics available?


        • charliex 1:41 am on May 12, 2015 Permalink

          We completed it (as far as one does with these things), its all available on the NSL 032 SVN http://wiki.032.la/nsl_svn_server in the juki folder

        • tom 2:58 am on May 12, 2015 Permalink

          Thanks! Can you offer any advice for using your software to control a stock machine with the addition of a camera? The standard 90deg head rotation is suitable for us but would really like the great features of programming your software offers.

        • charliex 3:00 am on May 12, 2015 Permalink

          it does support a camera for positioning, but depends on if registration vs part placement, i’d like at opencv etc for that. any decent usb camera works we used a pen type off ebay

        • tom 3:13 am on May 12, 2015 Permalink

          Is it necessary to replace the control board in the machine with the custom board or can the software work with standard hardware? It doesn’t look like the “Juki_PCB” EagleCAD design is routed or complete? I’m trying to figure out how to operate the stock hardware with this program before any vision work. Many thanks for any advice.

        • charliex 3:16 am on May 12, 2015 Permalink

          juki_shield is all you need, pop it on an arduino mega, the other board is just a mega with laid out at the right side.

          it does not work with the stock computer,, but it just plugs in.

        • tom 3:38 am on May 12, 2015 Permalink

          Ok, great; I’ve got an arduino mega 2560 on order and the sheild board ordered from osh park. The only other question I have now is what modifications are needed to use the stock head rotation? Thank you so much!

        • charliex 3:54 am on May 12, 2015 Permalink

          you’re making me think back now. one of the other members cad’d up a head for a little servo motor we had lying around and added that a step dir driver. you can see the details in part 3 of this

        • tom 4:19 am on May 12, 2015 Permalink

          Yes, I see the stepper motor addition to the head. I may do this in the future, but my question is can I use the shield board as-is to operate the existing head rotation valves? Can the software handle this too? Thank you.

        • charliex 4:26 am on May 12, 2015 Permalink

          yeah it should be able too, just find a spare IO and add it, since we’re using N degrees of rotation the software wouldn’t match identically

        • tom 7:03 am on May 12, 2015 Permalink

          It looks like the I/O are already mapped and routed for head rotate and center on the shield board fortunately. I didn’t see the use of the centering fingers in the videos. Was this ever accomplished? Thanks

        • charliex 10:04 pm on May 12, 2015 Permalink

          the io for the head would be to step dir the rotation, so it can be re-used if the software is recoded to do the 90o instead, which is fairly trivial.
          the squaring i don’t believe we never used, but that’s just GCODE.

        • tom 12:15 am on May 13, 2015 Permalink

          Thanks for the reply. Was USB interface completed or how is pc to arduino communication handled? I’ll be starting this all next week when the machine arrives and I’m getting the hardware together now.

        • charliex 12:36 am on May 13, 2015 Permalink

          the windows gui talks directly to the atmel chip via the faked serial port, there’s no arduino other than form factor.

        • tom 1:07 am on May 13, 2015 Permalink

          I see; so I should start with the “openpnp>firmware>grbl” project uploaded to the mega and use digital pins 0 and 1 (tx and rx) as a rs-232 com port? Thank you.

        • charliex 1:17 am on May 13, 2015 Permalink

          the mega will do an ftdi serial port to the host via usb.

        • tom 1:34 am on May 13, 2015 Permalink

          Thanks for your help I do appreciate it. My goal is to make it possible to use a stock machine with just removing the old PC hardware and installing the arduino/shield and camera. If I can get it done maybe I can get a few more retrofitted for my shop afterwards to make it more worth while. I’d be glad to share the work when it is done.

        • charliex 1:38 am on May 13, 2015 Permalink

          no worries, hope it works out for you. should be pretty straightforward, just make sure you do a lot of testing without the pickup head installed those things are unobtanium.

        • tom 1:18 pm on May 27, 2015 Permalink

          Hi Charlie,

          I’m digging deeper and found the 460 and 360 controller boards are not the same so I am revising the shield board for the 460. I have a few questions if you would be so kind:

          1. What is the grbl program compiled in? Atmel studio? I’m having trouble getting your code to compile in arduino ide due to “errors.”
          2. I’ve been unable to get the pc software to run; I get errors related to the opencv dll files (‘not valid image’ file.) Maybe I can try to use opencv’s latest version files?
          3. What is the part number for the head rotation servo? What type of driver is used to run it? Did you get a rotary encoder installed and how is that connected?

          Thank you

        • tom 1:24 pm on May 27, 2015 Permalink

          More careful reading reveals grbl built in avr studio and programmed over icsp; I’ll give this a shot!

        • charliex 4:46 pm on May 27, 2015 Permalink

          make sure you’re matching opencv dll versions to windows 32/64 bit. there may have been changes to opencv in the last 3 years that could have broken it. the motor was just one we had lying around, there is no encoder wheel, we either used an off the shelf stepper controller or made one out of an allegro controller.

    • Jack Gassett 8:49 pm on September 1, 2011 Permalink | Reply

      Hey Charlie,

      The thought has crossed my mind to replace the old Z80 computer with a Soft Z80 running on my Papilio FPGA board. 🙂

      The two problems with that are:
      1) The software as it is is not the greatest, seems like a lot of work and you would end up with the same software.
      2) It’s a lot of work that very few people would actually ever use.

      But, I love my local hackerspaces and if you think you can use a Papilio FPGA board I’d be happy to donate one to you guys.

    • charliex 9:04 pm on September 1, 2011 Permalink | Reply

      thanks Jack, I’ve got a few of your fpga boards already, got a few maybe three years ago? when we were all looking at the sump. we’re always happy to take more donations of dev boards though for people. http://wiki.032.la/nsl/Equipment_Inventory

      But we’re actually replacing it with an Stellaris ARM because TI gave us a bunch of free chips and dev boards, if we promised to make something cool. I’m writing the PC control software from scratch too and adding machine vision etc. I’ve got most of the board ready in eagle.

    • truthspew 9:40 pm on September 1, 2011 Permalink | Reply

      Wow, that is far too cool. Taking an old piece of tech and extending it’s usable life by upgrading it’s systems is priceless. I wish you great adventure with the machine!

    • Tim 3:40 pm on September 2, 2011 Permalink | Reply

      Very cool! How are you planning to implement the control software and vision system? I’ve been laying some groundwork for an open-source pick&place design, and the software controller is the next major step. (Right now this consists only of a Python to EMC2 remote interface and a few not-ready-for-primetime opencv experiments, but my freetime will free up again a bit this fall!)

      • charliex 9:53 pm on September 4, 2011 Permalink | Reply

        Longer term plan is to use a custom arm board, but last night we just built and designed a shield for the arduino mega. Vision is opencv currently

        • rfritz 3:44 pm on September 9, 2011 Permalink

          “Shims, we don’t think these are factory shims.” – They ARE factory shims.

          “The strange piece of string inside the head… What could it be for?” – It could be and in fact IS an oil wick. Google JUKI to find out what they made b4 pp machines.

          Contact Marc LeLonge(sp?) [alphatronique.com] on Zevatech list, he completed the ARM controller w/ PC GUI a while back.

          There are also scanned manuals and exploded mechanical dwgs for machine and assy’s.

        • charliex 7:15 pm on September 9, 2011 Permalink

          We found out about the shims being factory last night oddly enough, however the string was there to hold some bits in place. I’ve already chatted to marc, his solution isn’t finished yet and we’re pertty much at the same stage he his, maybe even a little bit beyond it as we’re moving on to the machine vision.

    • rfritz 4:45 am on September 10, 2011 Permalink | Reply

      I have three 460 heads all with “the string”, just like in your photo. Two from Florida and one from Texas. Hmm.

      • charliex 5:34 am on September 10, 2011 Permalink | Reply

        yeah the oil string thing makes sense for the age of the machine, with modern lubricants you shouldn’t need it. our head was in a mess when we got it and had to rebuild it and it was held together with all sorts/ it wasn’t in the manual we could see either.

    • Marc Lalonde 6:19 pm on September 13, 2011 Permalink | Reply


      sorry charlies but my project not a same stage of your
      i have all my machine doing production since august
      and one of it whit vision and servo rotation on head

      Kit i put on ebay was intended to be easy to install and setup so i remove
      servo and vision since i quite hard to setup and expensive since i use cognex vision system
      optic and lighting was not easy task to setup (top and bottom vision)

      as for software issue that have make me crash my machine head it was fixed now
      so now i monitor head cylinder switch and stop all motor in hardware if head was not full up
      old soft version rely on soft but experience show me that if head was stuck down(no air)
      the protection was not good and may let motor move and cause damage

      but i not yet knot if i put back on ebay since commercial grade hardware software make it expensive
      and market was quite limited so seem that i will never recover R&D money i put on solution
      but at last i have all my 5 machine working (3 in production + 2 spare)
      so my project main goal was dome i have machine that operate like i what and take 3 minute to program

      also remeber that make machine move was the easy part , make it easy to operate ,user friendly
      and reliable was more difficult i have ~75k line of code firmware hardware for have it

      Best regard and good luck
      Marc lalonde
      Alphatronique inc.

      • charliex 6:38 pm on September 13, 2011 Permalink | Reply

        We have the head up down detection, it won’t move with the head down, it knows if its homed, it knows the size of the table, it knows if its not moving, it knows if it picked up a tool or not, it knows if it picked up a part or dropped it, it knows if the air supply is probably getting low and waits for the compressor to catch up. It knows if the emergency switch has been pushed and needs to rehome. We’re using half step, the controllers have been updated for newer ones, same for the motors.

        We’ve added all sorts of safety features, some of them weren’t in the original . everything that can be checked is checked. The machine vision is being added too, head rotation is being worked on last night.

        There is nothing fancy about it, its a very simple system it doesn’t take a lot of work to better the zevatech.

        All of this we’ve done in a couple of weeks.

    • Marc Lalonde 10:05 pm on September 13, 2011 Permalink | Reply


      as said before i have no bout about your hardware

      i just curious wly change drive and original stepper ?
      original 5 phase stepper have lot of torque @ hi speed compared to 2 phase stepper
      and still not sure about wly use half step ? original drive do 0.25mm/step
      (zevatech software handel only 0.5mm but it software limitation)
      but i found that resolution limitation not come from motor but from feeder mechanical variation (repetability issue)
      and solder past making part slick since head height and force was only limited by gravity

      Best regard

    • charliex 10:10 pm on September 13, 2011 Permalink | Reply

      Simply because we could and had the parts on hand in the lab. The new controllers also offer more features too, the guy i work with is a cnc magician so he’s the one driving the changes.

      We’re also planning to add more features to the machine, and get down to 0402 (or better) we’re working on our own feeder design.

      • Jose 10:42 pm on March 1, 2012 Permalink | Reply

        Hi Charliex, great work on the 360, I have a JUKI KP460 that also have rebuilt and doing some ggood work now, but I’m VERY interested on upgrading it to camera and GUI interface, eager to replace the 286 PC running it!!!! and the folppy drive, great tip, great work, I’m not a programmer but like to tinker, any info/tips on how to modify my unit servos to OpenPnP great. Also feeders, are you guys thinking to modify the feeders? to smartfeeders? and the tips, when you break were do you get the replacements from? I broke a few that I patched and modified to pick up up to 0603 components.

    • charliex 11:56 pm on March 1, 2012 Permalink | Reply

      As long as its stepdir openpnp will support it.

      We haven’t changed the feeders, just been buying them off ebay when they appear.

      tips we haven’t broken yet, but parts we just scav around from people.

    • Tom Winsemius 4:34 pm on April 24, 2015 Permalink | Reply

      We have a Zevatech 460. Are you still looking for one?

      • charliex 4:59 pm on April 24, 2015 Permalink | Reply

        Hey Tom,
        we’re a volunteer run hackerspace, so only if its a freebie basically.


      • Charles Bynaker 12:41 am on May 26, 2015 Permalink | Reply

        I am interested in a Zevatech 460

        • Tom Winsemius 4:52 pm on May 26, 2015 Permalink

          Sorry, but it has been sold

        • Charles Bynaker 11:49 pm on May 26, 2015 Permalink


Compose new post
Next post/Next comment
Previous post/Previous comment
Show/Hide comments
Go to top
Go to login
Show/Hide help
shift + esc