I hate answering my own question, but here I go. I hope I don't get get points for answering, that would be weird, only for accepting an answer? (BTW, I did not get any response on the Element14 forum.)
The solution is to use the DRAW command, not ROUTE. DRAW will place a wire segment, exactly where you specify (unlike ROUTE, which tries to connect to an unrouted airwire. ROUTE is essentially useless in a script.). The next issue is via's: I can't (or don't want to) distinguish between a manual via and an autorouted via, so I keep all via's that connect two (or more) manual wire segments. Other via's are deleted.
So what my final script does is:
prepare a ripup command
for all copper segments that are not 0.01 wide (the width I use for autorouting)
check both endpoints for a via at that location
prepare the via to be resurrected when it is visited the 2nd time
prepare a command that resurrects the copper segment
execute the prepared commands
Note that it will probably not work for more than two layers, nor for other things than wire segments at the copper layer.
IMHO the whole concept of the eagle ULP and command languages is troublesome. An ULP runs in a read-only environment, the only way it can affect the circuit, board or library is by creating a list of commands. This eliminates some useful programming techniques, but worse is that the commands were not designed to be easily created from an ULP. You need all kinds of transformations (in this case: coordinates, shape names) to translate from the ULP world to the CMD world.
(edit) Before you run this ULP, set the 'wire bend' selection to allow arbitrary angles, otherwise eagle will try to adapt the resurrected wires to the allowed angles, which can result in a bloody mess. IMHO this is another example of the problem with ULP/SCR.
This is the ULP code:
// gather the commands that must be run on exit
string RunOnExit = "";
void cmd( string s ) { RunOnExit += s + "\n"; }
// return an x or y position in the form that can be used in a command
real f( int x ){
board( B ) switch( B.grid.unit ) {
case 0: return u2mic(x);
case 1: return u2mm(x);
case 2: return u2mil(x);
case 3: return u2inch(x);
}
}
// return the string form of the a via's shape
string sn( int x ){
if( x == VIA_SHAPE_SQUARE ) return "square";
if( x == VIA_SHAPE_ROUND ) return "round";
if( x == VIA_SHAPE_OCTAGON ) return "octagon";
if( x == VIA_SHAPE_ANNULUS ) return "annulus";
if( x == VIA_SHAPE_THERMAL ) return "thermal";
return "unknown-via-shape";
}
// count the number of times x occurs in s
int n_ocurrences( string s, string x ){
int i, n = 0;
while( 1 ){
i = strstr( s, x );
if( i == -1 ) return n;
s = strsub( s, i + strlen( x ));
n++;
}
}
// add a via, but only when it is visited the second time
string via_list = "";
void add_via( int a, int b ){
// for all via's
board( B ) B.signals( S ) S.vias( V ){
// if the via is at the current location
if(( V.x == a ) && ( V.y == b )){
string s, coo;
// the coordinates of the via are used as its identification
sprintf( coo, "(%.6f %.6f)", f( V.x ), f( V.y ));
// if this is the second visit to this via
via_list += coo;
if( n_ocurrences( via_list, coo ) == 2 ){
// resurrect this via
sprintf( s, "VIA '%s' %f %s %s;",
S.name, f( V.drill ), sn( V.shape[ 1 ] ), coo );
cmd( s );
}
}
}
}
if( !board ){
dlgMessageBox("start this ULP in Board", "OK");
exit( 0 );
}
board( B ){
// first delete all coper segments,
// later we will resurrect what we want to keep
cmd( "RIPUP;" );
// for all wire segments in the top and bottom copper layers
B.signals(S) S.wires(W) {
if( ( W.layer == 1 ) || ( W.layer == 16 ) ){
// that are not 0.01 width (that is what the autorouter uses)
if( f( W.width ) != 0.01 ){
string s;
// resurrect via's adjacent to this wire segment
add_via( W.x1, W.y1 );
add_via( W.x2, W.y2 );
sprintf( s, "CHANGE LAYER %d;", W.layer );
cmd( s );
// resurrect this wire segment
sprintf(
s, "WIRE '%s' %f (%.6f %.6f) (%.6f %.6f);",
S.name, f( W.width),
f(W.x1), f(W.y1), f(W.x2), f(W.y2));
cmd( s );
}
}
}
// dlgMessageBox( RunOnExit, "OK");
exit( RunOnExit );
}
Best Answer
Key in routing a PCB is part placement. It can be made almost impossible by poor part placement.
Looking at it a bit ...
IC1 looks like it needs rotating 180 degrees, and then move R1 and LED1 out of the way, to the right of IC1, and they can be routed under IC2.
Rotate SV1 180, and I think it will mostly go.
Edit: Use a few jumper-wires, to connect areas of copper which need to be connected, but can't be routed on a single sided board. I can't quite tell as some air-wires are a bit obscured, but I think you might need a couple.
A 'pretty solution' which disguises jumper wires are 0ohm resistors. Unfortunately you are using SMD resistors which don't have enough space between their pads to be used to cross over a wire :-(
Edit:
When I am trying to understand part placement, I sometimes do a rough/quick routing for the many-pins parts first. Then see where the two pin parts go. That seems to help me see what is going on topologically.
I always set the grid to a useful value. That saves a lot of time and effort in Eagle.
For rough routing, I set the grid size to track+space, then it is very quick to lay tracks near each-other, without breaking a Design Rule. I often set the grid 'alt' to an even divisor of that, e.g. if track+grid is 16, then Alt might be 4. That is usually good enough; diagonal corners might be slightly further apart than 'perfect', but it is easy, quick and safe.
When I do a rough/quick routing, I assume I'll rip up all tracks. I don't necessarily complete tracks. I often only route enough to 'uncross' tracks so that it is easy to see which tracks are causing problems, and which groups might route simply and together. I am looking for opportunities to move parts to simplify routing. I expect to rip up the tracks, and hence don't waste much effort.
I believe professionals say 'shallow, 45 degree corners are good, 90 degree or sharper are bad'. So I very rarely use the 90-degree wire bends. Being able to route tracks in parallel is quick. So I rarely use arbitrary angle wire bends. It is harder to rip-up and move curved bends, so I rarely use them either. That means I only use the two 45 degree wire bends for the majority of routing.
Important: Eagle was designed many years ago, and has a user interface optimised for heavy use, when the command line was still popular. A one button mouse or track pad is awful. Use a two button mouse, preferably with a wheel. Many commands become much easier, and zooming with the wheel becomes convenient. The second button helps with moving parts, rotating, selecting wire bends, ...
Useful: Bind common actions, which require lots of clicking to function keys. I have mine set to show various combinations of layers. One key shows all the layers that I'll have manufactured. Another removes all text, but leaves 'stop' to make it easy to see what I am routing and what must not overlap. Another switches off top layer copper, etc. The way to figure out what to put onto a function key is to type the command into the command line, then paste it into the define key dialogue. Most of my layer views are on the same function key, modified by shift, control etc. to make it easy to find.
Useful: Eagles on-line help contains a lot of useful information. If you have a second screen, and can avid the screen real estate, keep it open, and use the search facility. I found a lot of little techniques that are buried in a 'ctrl-alt-click' that way, which has saved me much effort.
NB: I am doing sub 100MHz MCU's, which have the high-speed stuff on chip, so these strategies work okay. There are community members who can give much better guidance about the electronics of a PCB than me.