<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://girishji.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://girishji.github.io/" rel="alternate" type="text/html" /><updated>2023-08-19T06:54:28+00:00</updated><id>https://girishji.github.io/feed.xml</id><title type="html">girish</title><subtitle>Things I learn in my spare time.
</subtitle><author><name>{&quot;name&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;&quot;}</name><email></email></author><entry><title type="html">Vim Plugin using Vim9 Script</title><link href="https://girishji.github.io/2023/08/03/vim-plugin-howto.html" rel="alternate" type="text/html" title="Vim Plugin using Vim9 Script" /><published>2023-08-03T08:16:45+00:00</published><updated>2023-08-03T08:16:45+00:00</updated><id>https://girishji.github.io/2023/08/03/vim-plugin-howto</id><content type="html" xml:base="https://girishji.github.io/2023/08/03/vim-plugin-howto.html">&lt;p&gt;If you are a longtime user of &lt;a href=&quot;https://www.vim.org&quot;&gt;Vim&lt;/a&gt; there are occasions
where you may want to extend the functionality of Vim by writing your own
plugin. The release of &lt;a href=&quot;https://vimhelp.org/vim9.txt.html&quot;&gt;Vim9 script&lt;/a&gt; has
made the task less intimidating since the new scripting language &lt;a href=&quot;https://github.com/yegappan/VimScriptForPythonDevelopers&quot;&gt;resembles
Python&lt;/a&gt;.
This blog post is not a beginner guide but rather a collection of thoughts from
my experience developing 
&lt;a href=&quot;https://github.com/girishji/autosuggest.vim&quot;&gt;autosuggest&lt;/a&gt; and
&lt;a href=&quot;https://github.com/girishji/vimcomplete&quot;&gt;vimcomplete&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are on the fence deciding whether to implement your idea in
&lt;a href=&quot;https://www.lua.org&quot;&gt;Lua&lt;/a&gt; for
&lt;a href=&quot;https://neovim.io/&quot;&gt;Neovim&lt;/a&gt; or &lt;a href=&quot;https://vimhelp.org/vim9.txt.html&quot;&gt;Vim9script&lt;/a&gt;
for &lt;a href=&quot;https://www.vim.org&quot;&gt;Vim&lt;/a&gt;, I have some opinions. Even
though legacy Vim script works on both Vim and Neovim I intentionally did not
learn it because, well, it is just weird and unreadable.&lt;/p&gt;

&lt;p&gt;Both Lua and Vim9script are compiled into bytecode (unlike legacy script). I wrote a few
non-trivial plugins in Lua before switching over to Vim9script. I prefer the
latter because the code tends to be more compact, has more advanced language
features for functional programming, has
better regex support, and offers smoother interface to Vim’s APIs. But Lua is
its own fun language to program in and Neovim keeps experimenting with new
features. Ultimately it boils down to preference. If Vim9script tickles your
curiosity then read on.&lt;/p&gt;

&lt;h2 id=&quot;directory-structure&quot;&gt;Directory Structure&lt;/h2&gt;

&lt;p&gt;The first step in writing a plugin is to organize your folders. Vim expects
certain folder names. Here is a typical organization for github hosted
repositories.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;myplugin
├── LICENSE
├── README.md
├── autoload
│   ├── foo.vim
│   └── bar.vim
├── doc
│   └── myplugin.txt
└── plugin
    └── myplugin.vim
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Your main directory name should be the name of the plugin. Under that
directory, the plugin should have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugin&lt;/code&gt; and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autoload&lt;/code&gt; directory:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugin&lt;/code&gt; directory sets up the plugin. It should include the commands and
keybindings that you want in your plugin. The files in this directory are
sourced in alphabetically order (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:h load-plugins&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autoload&lt;/code&gt; directory holds the meat of the plugin. It is only loaded when
one of the commands defined in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugin&lt;/code&gt; directory gets called. On-demand
loading keeps Vim’s initialization faster.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;doc&lt;/code&gt; directory is optional but highly recommended. Learn the Vim help file
syntax. You could include even more information here than in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README.md&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;In addition, you may need an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import&lt;/code&gt; directory if you wish to export
functions for use in other plugins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note: Vim has extensive documentation. Anytime you have doubt over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; try
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:helpgrep foo&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:h foo&amp;lt;tab&amp;gt;&lt;/code&gt; (with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wildmenu&lt;/code&gt;) or use
&lt;a href=&quot;https://github.com/girishji/autosuggest.vim&quot;&gt;autosuggest&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Include the following boilerplate code at the top of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugin/myplugin.vim&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if !has('vim9script') ||  v:version &amp;lt; 900
    &quot; Needs Vim version 9.0 and above
    finish
endif
vim9script
g:loaded_myplugin = true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All other files should include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim9script&lt;/code&gt; at the top. In order to use
functions defined in another script you have to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import&lt;/code&gt; directive.
From a script in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugin&lt;/code&gt; directory you could define a command as follows.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import autoload '../autoload/foo.vim'
command! -nargs=0 MyCommand foo.SomeFunction()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SomeFunction()&lt;/code&gt; needs to be exported (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:h vim9-export&lt;/code&gt;)
from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo.vim&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, make use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:h User&lt;/code&gt;) auto-command event to synchronize parts of initialization.&lt;/p&gt;

&lt;h2 id=&quot;language-features&quot;&gt;Language Features&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://vimhelp.org/vim9.txt.html&quot;&gt;Vim9script&lt;/a&gt; is fairly easy to learn. You
can also pick up some &lt;a href=&quot;https://github.com/lacygoill/wiki/blob/main/vim/vim9.md&quot;&gt;advanced
insights here&lt;/a&gt;. If you
are familiar with Python there is &lt;a href=&quot;https://github.com/yegappan/VimScriptForPythonDevelopers&quot;&gt;VimScript For Python
Developers&lt;/a&gt;. Finally,
here are some suggestions to make your programming task more fun.&lt;/p&gt;

&lt;h3 id=&quot;lambda-expressions&quot;&gt;Lambda Expressions&lt;/h3&gt;

&lt;p&gt;If you are doing any type of data manipulation lambda expressions (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:h lambda&lt;/code&gt;)
come in handy. You can use them with usual suspects &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reduce()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sort()&lt;/code&gt; etc.&lt;/p&gt;

&lt;p&gt;Functions can be chained using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&amp;gt;&lt;/code&gt; operator. Use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arg-&amp;gt;func()&lt;/code&gt; idiom consistently
throughout. Keep your code readable and bloat-free.&lt;/p&gt;

&lt;h3 id=&quot;meta-tables&quot;&gt;Meta Tables&lt;/h3&gt;

&lt;p&gt;Vim9script will have &lt;em&gt;class&lt;/em&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:h class&lt;/code&gt;) in the near future. In the meantime, you can emulate
an object (encapsulation) using a simple dictionary and function references
(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:h funcref()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:h function()&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def NewMyObject(someArg: bool): dict&amp;lt;any&amp;gt;
    var contents = {
        property1: [],
        property2: someArg,
    }
    contents-&amp;gt;extend({
        functionName1: function(FunctionName1, [contents]),
    })
    return contents
enddef

def functionName1(obj: dict&amp;lt;any&amp;gt;, optionalArg: number)
    var foo = obj.property1
enddef

var myObj = NewMyObject(true)
var fnArg = 22
myObj.functionName1(fnArg)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;options&quot;&gt;Options&lt;/h3&gt;

&lt;p&gt;Users of your plugin may need to set options. Use a dictionary to encapsulate
all options. It is best not to use a global variable for each option, since they
pollute global namespace. You can use a global function that takes a dictionary argument to
set the options. There is also a possibility to use exported function, but this
limits the users to only use Vim9script for configuration. So the former method
is preferred.&lt;/p&gt;

&lt;p&gt;In your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;autoload/options.vim&lt;/code&gt; you can define the options.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;export var myOptions: dict&amp;lt;any&amp;gt; = {
    option1: '',
    option2: false,
    option3: [],
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugin/myplugin.vim&lt;/code&gt; define a global function to set options.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import autoload '../autoload/options.vim'

def! g:PluginNameOptionsSet(opts: dict&amp;lt;any&amp;gt;)
    options.myOptions-&amp;gt;extend(opts)
enddef
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;strings&quot;&gt;Strings&lt;/h3&gt;

&lt;p&gt;Be familiar with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;==#&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;==?&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=~#&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=~?&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!~&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;matchstr()&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;matchlist()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\c&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\v&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;'&lt;/code&gt; etc. See help files.&lt;/p&gt;

&lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$'sometext {var}'&lt;/code&gt; as opposed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;sometext &quot; .. var&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;debug&quot;&gt;Debug&lt;/h3&gt;

&lt;p&gt;For the most part &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echom&lt;/code&gt; in scripts is adequate. You can view the messages
using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:messages&lt;/code&gt;. For checking regex you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:echo
'teststring' =~ 'pattern'&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:echo matchstr('foo', 'pattern')&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;disassemble&quot;&gt;Disassemble&lt;/h3&gt;

&lt;p&gt;Sometimes you may ask yourself if it is worth caching a dictionary key
value outside a loop, and discover that Vim9 compiler does not do loop optimization.
You can verify using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:disassembly&lt;/code&gt; that it uses LOADSCRIPT and
USEDICT instructions inside the loop when value is not
cached.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% vim -Nu NONE -S &amp;lt;(cat &amp;lt;&amp;lt;'EOF'                                                                                       1 :(   
    vim9script
    var foo = {x: 1, y: 2}
    def Func()
        var fooy = foo.y
        for i in range(5)
            # echom fooy     # good: using cached value
            echom foo.y      # not good
        endfor
    enddef
    disa Func
EOF
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;asynchronous-execution&quot;&gt;Asynchronous Execution&lt;/h3&gt;

&lt;p&gt;Vim offers &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;job_start()&lt;/code&gt; for truly parallel execution (on a multicore system). You can spawn a new
process and wait for output. However, there is also a lightweight option that offers
concurrent execution. It may sound non-intuitive but you can use
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timer_start()&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; timeout to schedule your function for execution at a
later time when Vim’s main-loop is free. You can also pass arguments to
callback, for example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timer_start(0, function(MyWorker, [arg1, arg2]))&lt;/code&gt;.
Typically you break down your long running task into batches (say you want to
search a few thousand lines then search in batches of a thousand lines each).
Each batch can be scheduled using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timer_start()&lt;/code&gt; as above, with one of the arguments to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyWorker()&lt;/code&gt; being the index into a batch data array. These tasks should be chained by
having &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyWorker()&lt;/code&gt; call the next &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timer_start()&lt;/code&gt; (for the next batch). The
magic here is that Vim schedules any newly typed keystrokes between each
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyWorker()&lt;/code&gt; invocation. Even though Vim is single-threaded your plugin remains
responsive to keystrokes even while working on a long running task.&lt;/p&gt;

&lt;p&gt;For demonstration, see
&lt;a href=&quot;https://github.com/girishji/autosuggest.vim/blob/main/autoload/search.vim&quot;&gt;autosuggest&lt;/a&gt; or
&lt;a href=&quot;https://github.com/girishji/ngram-complete.vim/blob/main/autoload/unigram.vim&quot;&gt;ngram-complete&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;performance-measurement-and-timing&quot;&gt;Performance Measurement and Timing&lt;/h3&gt;

&lt;p&gt;Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reltime()&lt;/code&gt; before and after a code section measures execution time.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var start = reltime()
# ... do something ...
echom $'Elapsed time: {start-&amp;gt;reltime()-&amp;gt;reltimestr()}'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can abort a task if it goes over a timeout. Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reltimefloat()&lt;/code&gt; for
that.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const timeout = 2000 # millisec
var start = reltime()
while (start-&amp;gt;reltime()-&amp;gt;reltimefloat() * 1000) &amp;lt; timeout
    # ... process a batch ...
endwhile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;expose-apis-to-other-plugins&quot;&gt;Expose APIs to Other Plugins&lt;/h3&gt;

&lt;p&gt;Say you want other plugins to be able to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FooAPI()&lt;/code&gt; function of your
plugin. Create a directory named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import&lt;/code&gt; at the top level and a file inside it
(say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fooplugin.vim&lt;/code&gt;). Inside this file you can expose your APIs by exporting
functions.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vim9script
import '../autoload/myfoo.vim'

export def FooAPI()
    myfoo.MyService()
enddef
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Other plugins can import your API.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import 'fooplugin.vim'
def MyFunc()
    fooplugin.FooAPI()
enddef
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For demonstration, see API in
&lt;a href=&quot;https://github.com/girishji/vimcomplete/blob/main/import/vimcompletor.vim&quot;&gt;vimcomplete&lt;/a&gt;
used in
&lt;a href=&quot;https://github.com/girishji/ngram-complete.vim/blob/main/plugin/ngram-complete.vim&quot;&gt;ngram-complete&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;autocompletion-for-vim9-script&quot;&gt;Autocompletion for Vim9 Script&lt;/h3&gt;

&lt;p&gt;LSP for Vim9 script does not exist yet. In the meantime, you can use the
autocompletion extension for Vim9 script language
&lt;a href=&quot;https://github.com/girishji/vimscript-complete.vim&quot;&gt;vimscript-complete&lt;/a&gt;.&lt;/p&gt;</content><author><name>{&quot;name&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;&quot;}</name></author><category term="vim" /><category term="vim9script" /><category term="vim-plugin" /><category term="howto" /><summary type="html">If you are a longtime user of Vim there are occasions where you may want to extend the functionality of Vim by writing your own plugin. The release of Vim9 script has made the task less intimidating since the new scripting language resembles Python. This blog post is not a beginner guide but rather a collection of thoughts from my experience developing autosuggest and vimcomplete.</summary></entry><entry><title type="html">Keyboard: How Quickly Can You Press a Key</title><link href="https://girishji.github.io/2023/01/16/keypress-time.html" rel="alternate" type="text/html" title="Keyboard: How Quickly Can You Press a Key" /><published>2023-01-16T08:16:45+00:00</published><updated>2023-01-16T08:16:45+00:00</updated><id>https://girishji.github.io/2023/01/16/keypress-time</id><content type="html" xml:base="https://girishji.github.io/2023/01/16/keypress-time.html">&lt;p&gt;If you ever wondered whether a key-press happens in milliseconds or microseconds
in a keyboard wonder no more.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TLDR; It takes several milliseconds depending on the switch and finger speed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is measured?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A switch has pre-activation (pre-travel), activation and post-activation
distance. In case of Gateron Yellow optical switches, so called ‘speed’
switches, pre-activation is about 1mm, activation happens for another 1mm
(height of optical elements) and post-activation distance is about 1.2mm. For
other Gateron MX style switches pre-activation is 2mm, activation is 1mm, and
post-activation is 2mm. Since the setup uses voltage waveform from an optical
switch as a trigger only the activation process is subject to measurement. Time
taken for pre-activation and post-activation are not accounted for. Secondly,
the switch actuator does not have to travel all the way through the actuation
zone for the state change to be registered in the microcontroller. The GPIO pin
registers a “high” when voltage rises to ~0.8V and “low” when voltage falls
below ~2V.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/tsetup1.jpeg&quot; alt=&quot;Setup&quot; width=&quot;400&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The test setup consists of a highly responsive optical switch. The
phototransistor has rise and fall time of a few nanoseconds. You can verify from
the oscilloscope screen. Yellow line represents voltage drop across the
phototransistor while green line is the pulse applied to IR LED. Voltage drop
across phototransistor is inverted owing to the nature of the circuit. Each
horizontal division is 1us.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/fastsw1.png&quot; alt=&quot;phototransistor rise time&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Before measuring key-press time IR LED is driven from a constant voltage source.
When key is pressed IR light is interrupted from entering the base junction of
phototransistor. This corresponds to fall time and since the plot is reversed
the yellow line rises.&lt;/p&gt;

&lt;p&gt;The switch used is of Gateron Yellow optical linear variety. About 10
measurements are taken but only a representative sample is presented here.&lt;/p&gt;

&lt;h3 id=&quot;fast-key-press&quot;&gt;Fast Key Press&lt;/h3&gt;

&lt;p&gt;It takes about 100us for fall time. Each horizontal division is 100us and
vertical division is 2V. Voltage rises from ~0V to ~3.3V. You can observe that
voltage rises very quickly to 0.8V, in less than 50us. This is when GPIO pin
will register state change. These types of fast key-presses are rare in normal
typing though.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/fastkp.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;average-key-press&quot;&gt;Average Key Press&lt;/h3&gt;

&lt;p&gt;It takes about 200us to 2ms. Each horizontal division is 100us and vertical
division is 2V as above. Normal typing could be characterized as average.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/avkp.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;lazy-key-press&quot;&gt;Lazy Key Press&lt;/h3&gt;

&lt;p&gt;It takes over 2ms. Each horizontal division is 1ms and vertical division is 2V.
This is generally rare for switch with lighter springs but quite common for
switch with heavier springs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/lazykp.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;average-key-release&quot;&gt;Average Key Release&lt;/h3&gt;

&lt;p&gt;It takes about the same time as key press. Each horizontal division is 100us.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/avkr.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;note&quot;&gt;Note&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Although no serious statistical evidence is compiled and sample size is just
&lt;em&gt;one&lt;/em&gt;, averaging over 10 key-presses a switch with lighter spring (15gf)
resulted in faster key-presses compared to heavier Yellow (35gf) and Red
(45gf) switches.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With 15gf it is possible (although rare) to get sub 100us key-presses.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/superfastkp.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;There is generally a clean rise and fall curve of voltage drop across
phototransistor. This is the reason why optical keyboards do not require
debouncing. Even though there is a transition region of voltage (0.8v-1.2v)
where GPIO pin can read unpredictable logical value, it is generally not the
case. If such problem indeed was encountered a comparator with hysteresis
(Schmitt trigger) can be added to the circuit.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;em&gt;How fast can an optical switch detect a key-press?&lt;/em&gt; As you may have noticed
the optical switch used in this experiment has extremely low rise/fall time.
So the key event is detected as soon as the voltage crosses TTL logic
threshold. The switch uses about 2mA current (for IR LED and phototransistor
combined). Given that USB can supply 500mA, all the switches in the keyboard
can remain powered up all the time. This linear (non-switched) circuit is only
limited by how soon the microprocessor can read the GPIO pins. On Nordic’s
nrf52840 MPU a tight loop can read GPIO ‘ports’ (which contain a group of GPIO
pins) all at once at 460kHz frequency (see pic). Suffice to say an optical
switch will not be on the critical path.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/mindelay.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;This test does not measure keyboard latency. After the microprocessor detects
state change event it has to ship the data to the host computer. Since
keyboards are usually configured as HID there will be at least 125us polling
delay if the USB port is configured as HS (high speed) USB or (most likely the
case) 1ms delay if port is FS (full speed) USB. The application on the host
computer will have additional delay as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;updated-jan-21-2023&quot;&gt;UPDATED Jan 21, 2023&lt;/h2&gt;

&lt;p&gt;After the above experiment I was curious to find out if the voltage across
phototransistor changes throughout the 1mm height through which the switch
actuator travels while closing the aperture available for IR light to pass
through. The result might surprise you. I used the above setup but changed the
input pin to ADC (analog to digital converter). When the actuator is pressed,
voltage slowly rises from 0V to 3.3V. The
&lt;a href=&quot;https://github.com/girishji/zephyr-xiao-ble&quot;&gt;ADC on nRF52840&lt;/a&gt; is capable of 16
bit measurement, which provides adequate resolution.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;IR light from LED is so narrow that only about 0.1mm to 0.2mm distance of
actuator actually participates in voltage change.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a far cry from 1mm of actuation distance that was anticipated. As the
key is pressed voltage remains at 0V until the critical distance of 0.1mm to
0.2mm through which voltage suddenly rises to 3.3V. This spot is at the same
height as the center of the IR lens. All the key-press numbers measured above
are relevant for only 0.1mm to 0.2mm of actual key travel. Let’s say it took
200us to travel 0.2mm distance. This translates to finger speed of 1m/s – not
unreasonable.&lt;/p&gt;

&lt;p&gt;Instead of referring to actuation distance we can call this actuation point.
This point is about 1.5mm from the top of the switch for Gateron Yellow and
about 2.5mm for Gateron Red optical switches. Finger do not travel at a constant
speed. It accelerates from standing start. One can guess that it would take 1ms
to over 2ms to register a key-press event from the moment finger starts moving,
in an optical switch setup with infinitesimal (nanoseconds) rise time. In a
mechanical switch (with electrical contacts), there is additional 5ms to 7ms
delay owing to debouncing.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It would take 1ms to over 2ms to register a key-press event from the moment
finger starts moving, in an optical switch setup with infinitesimal
(nanoseconds) rise time.&lt;/p&gt;

&lt;hr /&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/girishji/zephyr-xiao-ble&quot;&gt;GPIO and ADC setup for Seeed XIOA BLE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>{&quot;name&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;&quot;}</name></author><category term="optical" /><category term="keyboard" /><category term="switch" /><summary type="html">If you ever wondered whether a key-press happens in milliseconds or microseconds in a keyboard wonder no more.</summary></entry><entry><title type="html">KiCad 6 Python Scripting: Place Footprints, Create Tracks, Curved Tracks, Vias, and Edge Cut Lines</title><link href="https://girishji.github.io/2022/08/17/kicad-python-footprints-curved-tracks-edge-cuts.html" rel="alternate" type="text/html" title="KiCad 6 Python Scripting: Place Footprints, Create Tracks, Curved Tracks, Vias, and Edge Cut Lines" /><published>2022-08-17T10:16:45+00:00</published><updated>2022-08-17T10:16:45+00:00</updated><id>https://girishji.github.io/2022/08/17/kicad-python-footprints-curved-tracks-edge-cuts</id><content type="html" xml:base="https://girishji.github.io/2022/08/17/kicad-python-footprints-curved-tracks-edge-cuts.html">&lt;p&gt;If you find yourself in a situation where you are placing component footprints
at multiple locations on PCB using &lt;a href=&quot;https://www.kicad.org/&quot;&gt;KiCad&lt;/a&gt;, or routing a pattern of
tracks repeatedly (like in a keyboard) you’ll save time by automating through a
Python script.&lt;/p&gt;

&lt;h3 id=&quot;contents&quot;&gt;Contents&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#Introduction&quot;&gt;Introduction&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#how-to-run-python-script&quot;&gt;How to Run Python Script&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#kicad-coordinate-system&quot;&gt;KiCad Coordinate System&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#footprints&quot;&gt;Footprints&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#place-footprint&quot;&gt;Place Footprint&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#tracks&quot;&gt;Tracks&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#straight-track&quot;&gt;Straight Track&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#curved-track&quot;&gt;Curved Track&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#create-via&quot;&gt;Create Via&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#remove-all-tracks-and-vias&quot;&gt;Remove All Tracks and Vias&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#edge-cuts&quot;&gt;Edge Cuts&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#draw-line&quot;&gt;Draw Line&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#draw-arc&quot;&gt;Draw Arc&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#remove-all-lines&quot;&gt;Remove All Lines&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.kicad.org/&quot;&gt;KiCad 6&lt;/a&gt; has decent support for scripting but
&lt;a href=&quot;https://docs.kicad.org/doxygen-python/index.html&quot;&gt;documentation&lt;/a&gt; can be hard to grok. Reading their code is often
the only recourse. I’ll cover the basics of placing footprints and routing
tracks with code examples.&lt;/p&gt;

&lt;h3 id=&quot;how-to-run-python-script&quot;&gt;How to Run Python Script&lt;/h3&gt;

&lt;p&gt;Copy (or symlink) your python script to KiCad plugins directory, which (on a
Mac) is located in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Documents/KiCad/6.0/scripting/plugins&lt;/code&gt;. You can find out
where KiCad looks for plugins and scripts by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import pcbnew; print(pcbnew.PLUGIN_DIRECTORIES_SEARCH)&lt;/code&gt;
from Python Console in PCB Editor window (icon is at the right hand top corner).&lt;/p&gt;

&lt;p&gt;In the console window simply import the python module using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import filename&lt;/code&gt;
(if your python script is named filename.py). This will execute the script. It
works the first time, but Python interpreter will not import the same module
twice. There is a solution. You reload the module again using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import
importlib&lt;/code&gt; followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;importlib.reload(filename)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is an alternate approach of using plugins, but above method is simpler;
your print output appears in the console window instead of having it
redirected to a file.&lt;/p&gt;

&lt;h3 id=&quot;kicad-coordinate-system&quot;&gt;KiCad Coordinate System&lt;/h3&gt;

&lt;p&gt;The Cartesian plane KiCad uses has Y-axis pointing down and X-axis pointing to
the right. Moreover, distance is specified in millionth of millimeter, and
angles are in tenths of a degree. These aspects will become clear later. Points
in space are represented by &lt;a href=&quot;https://docs.kicad.org/doxygen-python/classpcbnew_1_1wxPoint.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wxPoint&lt;/code&gt;&lt;/a&gt;. There is also a millimeter
variant called &lt;a href=&quot;https://docs.kicad.org/doxygen-python/namespacepcbnew.html#af68fa631c5cc3b5f30e869f9951ab920&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wxPointMM&lt;/code&gt;&lt;/a&gt;, and a conversion routine &lt;a href=&quot;https://docs.kicad.org/doxygen-python/namespacepcbnew.html#a3b6e68db767968e491ebb6c2cd82e9c1&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fromMM()&lt;/code&gt;&lt;/a&gt;. Using mils
instead of millimeter is also possible but not covered here.&lt;/p&gt;

&lt;h2 id=&quot;footprints&quot;&gt;Footprints&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.kicad.org/doxygen-python/classpcbnew_1_1FOOTPRINT.html&quot;&gt;Footprints&lt;/a&gt; are needed to position components on pcb.&lt;/p&gt;

&lt;h3 id=&quot;place-footprint&quot;&gt;Place Footprint&lt;/h3&gt;

&lt;p&gt;Get a reference to the footprint object from the
&lt;a href=&quot;https://docs.kicad.org/doxygen-python/classpcbnew_1_1BOARD.html&quot;&gt;Board&lt;/a&gt; object. You can then place the footprint and set the orientation by calling the
footprint object itself.&lt;/p&gt;

&lt;p&gt;In the following example we have three (SMD) footprints, for 2 resistors and a diode.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPointMM&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Get reference to footprint objects
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Place footprints
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wxPointMM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# (x, y) = (20, 20) in mm
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetOrientation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# rotate by 90 deg
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wxPointMM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wxPointMM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;26&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Update display
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic1.png&quot; alt=&quot;&quot; width=&quot;250&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;tracks&quot;&gt;Tracks&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.kicad.org/&quot;&gt;KiCad 6&lt;/a&gt; supports straight line as well as curved tracks.&lt;/p&gt;

&lt;h3 id=&quot;straight-track&quot;&gt;Straight Track&lt;/h3&gt;

&lt;p&gt;To route a track, you need a start and end &lt;a href=&quot;https://docs.kicad.org/doxygen-python/classpcbnew_1_1wxPoint.html&quot;&gt;point&lt;/a&gt;. You need to also locate the
center of the pads that terminate the track.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPointMM&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F_Cu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PCB_TRACK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.25&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1e6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Route track from pad #1 of footprint R1 to pad #1 of D1 with 45-deg corner
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindPadByNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindPadByNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thru&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thru&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thru&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic2.png&quot; alt=&quot;&quot; width=&quot;250&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;curved-track&quot;&gt;Curved Track&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.kicad.org/&quot;&gt;KiCad 6&lt;/a&gt; has support for drawing curved tracks, be it circular arcs or Bezier curves.
Only circular arcs are covered here. Use &lt;a href=&quot;https://docs.kicad.org/doxygen-python/classpcbnew_1_1PCB__ARC.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PCB_ARC&lt;/code&gt;&lt;/a&gt; object and specify start, mid and end points of arc.&lt;/p&gt;

&lt;p&gt;For low frequency applications, curved tracks are mostly for aesthetic reasons. Moreover, during manual routing if 
you use the “shove” option KiCad may decide to convert rounded edges
to sharp corners. To manually route a rounded track use &lt;em&gt;Ctrl-/&lt;/em&gt; (or &lt;em&gt;Cmd-/&lt;/em&gt; on
Mac) shortcut to switch among following options: corners at 45 deg -&amp;gt; rounded corners at 45 deg
-&amp;gt; corners at 90 deg -&amp;gt; rounded corners at 90 deg, after you click on
the starting point of track.&lt;/p&gt;

&lt;p&gt;The following example adds a rounded corner to two straight line tracks.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;math&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPointMM&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_track_arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F_Cu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PCB_ARC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetMid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.25&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1e6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Route track from pad #2 of footprint R1 to pad #1 of R2
#   with 90-deg arc of radius 1.5mm
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IU_PER_MM&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindPadByNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindPadByNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Find the mid point of the arc by translating the origin to the center of arc
#   and rotating the axis by 45-deg
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;start1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radians&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;end1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;radians&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;theta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_track_arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic3.png&quot; alt=&quot;&quot; width=&quot;250&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;create-via&quot;&gt;Create Via&lt;/h3&gt;

&lt;p&gt;Create a via at 1mm offset from pad #2 of footprint R2 and connect a track to it.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPointMM&lt;/span&gt;
 
&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindPadByNumber&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;via_location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IU_PER_MM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;via_location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;via&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PCB_VIA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;via&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;via_location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;via&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetDrill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1e6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;via&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1e6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;via&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic4.png&quot; alt=&quot;&quot; width=&quot;250&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;remove-all-tracks-and-vias&quot;&gt;Remove All Tracks and Vias&lt;/h3&gt;

&lt;p&gt;You may need to remove stale tracks before adding new ones.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetTracks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;edge-cuts&quot;&gt;Edge Cuts&lt;/h2&gt;

&lt;p&gt;Edge Cut lines define the boundary of the pcb. &lt;a href=&quot;https://www.kicad.org/&quot;&gt;KiCad 6&lt;/a&gt; has support for drawing
straight lines and arcs on any layer, not just on Edge Cuts. To draw a line you
specify the end points. To draw an arc you specify starting point, center of
the arc, and the angle. This API is slightly different from drawing curved
tracks where you specify mid-point of the curve. There is also API to draw
Bezier curves.&lt;/p&gt;

&lt;h3 id=&quot;draw-line&quot;&gt;Draw Line&lt;/h3&gt;

&lt;p&gt;Use &lt;a href=&quot;https://docs.kicad.org/doxygen-python/classpcbnew_1_1PCB__ARC.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PCB_SHAPE&lt;/code&gt;&lt;/a&gt; object and set the shape to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHAPE_T_SEGMENT&lt;/code&gt;.
You can specify the layer and line width. Use the search box in the
&lt;a href=&quot;https://docs.kicad.org/doxygen-python/index.html&quot;&gt;documentation&lt;/a&gt; to search for symbols.&lt;/p&gt;

&lt;p&gt;Add an edge cuts border around the components. Draw lines on all four
sides and connect them by rounded corners.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPointMM&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge_Cuts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PCB_SHAPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHAPE_T_SEGMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IU_PER_MM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;segment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IU_PER_MM&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IU_PER_MM&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic5.png&quot; alt=&quot;&quot; width=&quot;250&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;draw-arc&quot;&gt;Draw Arc&lt;/h3&gt;

&lt;p&gt;Use &lt;a href=&quot;https://docs.kicad.org/doxygen-python/classpcbnew_1_1PCB__ARC.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PCB_SHAPE&lt;/code&gt;&lt;/a&gt; object and set the shape to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHAPE_T_ARC&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPointMM&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_line_arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;angle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Edge_Cuts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PCB_SHAPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetShape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHAPE_T_ARC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetStart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetCenter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetArcAngleAndEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;angle&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetLayer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetWidth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IU_PER_MM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IU_PER_MM&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IU_PER_MM&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;R2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;d1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FindFootprintByReference&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPosition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_line_arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_line_arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_line_arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;center&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wxPoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;add_line_arc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pic6.png&quot; alt=&quot;&quot; width=&quot;250&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;remove-all-lines&quot;&gt;Remove All Lines&lt;/h3&gt;

&lt;p&gt;You may want start with a fresh slate.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pcbnew&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetBoard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dr&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetDrawings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;pcbnew&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Refresh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;KiCad 6 provides adequate scripting capability for designing pcb’s of moderate complexity.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;KiCad projects that use Python scripts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/girishji/optical-keyboard-mx&quot;&gt;Optical keyboard with MX switches&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/girishji/keychron-optical-keyboard&quot;&gt;Optical keyboard with low profile switches&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name>{&quot;name&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;&quot;}</name></author><category term="kicad" /><category term="kicad6" /><category term="python" /><category term="scripting" /><summary type="html">If you find yourself in a situation where you are placing component footprints at multiple locations on PCB using KiCad, or routing a pattern of tracks repeatedly (like in a keyboard) you’ll save time by automating through a Python script.</summary></entry><entry><title type="html">Optical Keyboard: Circuit Design</title><link href="https://girishji.github.io/2022/08/17/optical-keyboard-design.html" rel="alternate" type="text/html" title="Optical Keyboard: Circuit Design" /><published>2022-08-17T10:16:45+00:00</published><updated>2022-08-17T10:16:45+00:00</updated><id>https://girishji.github.io/2022/08/17/optical-keyboard-design</id><content type="html" xml:base="https://girishji.github.io/2022/08/17/optical-keyboard-design.html">&lt;p&gt;This guide will walk you through some concepts that will help you design a
basic optical keyboard PCB.  No special knowledge is assumed beyond engineering
basics. The circuits used here are rudimentary and they will provide
performance comparable to a mechanical keyboard. Low latency specialized
circuits are not covered in this guide.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/opic8.png&quot; alt=&quot;image&quot; title=&quot;Keyboard with low-profile Keychron optical switches&quot; width=&quot;275&quot; /&gt; &lt;img src=&quot;/assets/opic7.png&quot; alt=&quot;image&quot; title=&quot;Keyboard with low-profile Keychron optical switches&quot; width=&quot;275&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can think of optical switches as apertures through which infra-red (IR)
light (from LED) is allowed to pass through and shine on a phototransistor
(PT). When switch is pressed the aperture window closes and stops the light
from being irradiated on PT. PT generates current in response to light. A
photodiode can also be used in place of phototransistor, but we will limit the
discussion to PT. A PT is a combination of photodiode and another junction
diode, making it a NPN transistor whose base is exposed to light. The current
generated by PT has a near linear relationship with the amount of light shone
on the base.&lt;/p&gt;

&lt;p&gt;A PT is an analog device that produces current. This current needs to be
converted to voltage at appropriate digital
&lt;a href=&quot;https://learn.sparkfun.com/tutorials/logic-levels/all&quot;&gt;TTL&lt;/a&gt; levels before the
‘state’ of the switch (on/off) can be read by the GPIO (general purpose
input-output) pin of a microcontroller. We use a passive device like a resistor
to convert current to voltage. On the other hand, if you were to use a
photodiode (which produces very small current) an active device like an OpAmp
is needed to convert current to voltage (transimpedence).&lt;/p&gt;

&lt;p&gt;We are interested in only two states (on/off) for digital operation. We are not
interested in partial keypresses for instance (analog operation). So we drive
the PT between saturation (on) and cutoff (off) states.&lt;/p&gt;

&lt;h2 id=&quot;circuit&quot;&gt;Circuit&lt;/h2&gt;

&lt;p&gt;There are two ways you can measure voltage drop across PT. The circuit shown in
Figure (A) is a common-emitter amplifier, with IR LED shown on left and PT on the
right. Light input at the base causes the output (&lt;strong&gt;Vout&lt;/strong&gt;) to decrease from
high to low. If you were to connect &lt;strong&gt;Vout&lt;/strong&gt; to the input pin of MCU it would
read LOW to HIGH when switch is depressed. The circuit shown in Figure (B) is a
common-collector amplifier with an output (&lt;strong&gt;Vout&lt;/strong&gt;) increasing from low to
high in response to light input. In this case MCU pin will read HIGH to LOW.
For these circuits to operate in the switching mode, &lt;strong&gt;Vcc&lt;/strong&gt; &amp;lt; &lt;strong&gt;RL&lt;/strong&gt; × &lt;strong&gt;Ic&lt;/strong&gt;,
where &lt;strong&gt;Ic&lt;/strong&gt; is the maximum anticipated current and &lt;strong&gt;Vcc&lt;/strong&gt; is the supply
voltage.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/opic1.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;what-are-the-values-of-r-and-rl&quot;&gt;&lt;em&gt;What are the values of &lt;strong&gt;R&lt;/strong&gt; and &lt;strong&gt;RL&lt;/strong&gt;?&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;Majority of current is consumed by the IR LED. First we have to determine how much
current is available for each IR LED, and then we can use Ohm’s law to calculate
&lt;strong&gt;R&lt;/strong&gt;. As a USB ‘device’ a keyboard is allocated 500mA current. We can budget
up to 300 mA for optical switches for instance, and use the rest for
microcontroller and backlight. We can allocate the current budget to each IR LED
based on the matrix we choose. This will become clear later.  &lt;strong&gt;RL&lt;/strong&gt; is chosen
by trial-and-error such that multimeter reads TTL level voltage when switch is
on/off. Value of &lt;strong&gt;RL&lt;/strong&gt; depends on the brand/type of PT and amount of IR light
falling on PT. Here are some values for Everlight IR12-21C/TR8 and PT12-21B/TR8
combo. Please do your own experiment to fine tune &lt;strong&gt;RL&lt;/strong&gt;. You can also use 5V
source directly from USB instead of 3V3 for powering IR LED.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;3.3v&lt;/strong&gt;:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;strong&gt;R&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;I&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;RL&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Ic&lt;/strong&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;540&lt;/td&gt;
      &lt;td&gt;4.2 mA&lt;/td&gt;
      &lt;td&gt;4k&lt;/td&gt;
      &lt;td&gt;0.83 mA&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1.5k&lt;/td&gt;
      &lt;td&gt;1.4 mA&lt;/td&gt;
      &lt;td&gt;27k&lt;/td&gt;
      &lt;td&gt;0.18 mA&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2.2k&lt;/td&gt;
      &lt;td&gt;1.0 mA&lt;/td&gt;
      &lt;td&gt;45k&lt;/td&gt;
      &lt;td&gt;0.1 mA&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;ul&gt;
  &lt;li&gt;Voltage across &lt;strong&gt;R&lt;/strong&gt; is ~2.14V&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;I&lt;/strong&gt; = 2.14/&lt;strong&gt;R&lt;/strong&gt;, and &lt;strong&gt;Ic&lt;/strong&gt; ~ 3.3/&lt;strong&gt;RL&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;R&lt;/strong&gt; and &lt;strong&gt;RL&lt;/strong&gt; are in ohms&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;rise-and-fall-time&quot;&gt;Rise and Fall Time&lt;/h2&gt;

&lt;p&gt;Recall that a PT is also a transistor. Transistor provides current
amplification but also brings its peculiarities. The time needed for PT to
reach saturation and thereby rise the voltage (say from 0V to 3V) is called
‘rise’ time. More specifically, rise time is the time needed for PT to rise
from 10% of LOW value to 90% of HIGH value. Similary ‘fall’ time is defined for
the reverse situation. In addition, there is ‘storage’ time which is the time
the transistor stays on even after the base current (in this case light
incident on the base) has been removed. Storage+fall time is slightly larger
than rise time because transistor junction behaves like a capacitor. The charge
stored in the base junctions must be removed before the transistor will turn
off. The deeper the saturation of transistor the longer the turn off time.
Rise, storage and fall time of PT directly impacts the latency of a keyboard.&lt;/p&gt;

&lt;p&gt;How quickly does a PT rise and fall? Here are some illustrative examples for a
PT driven to saturation. The circuit is the common-emitter amplifier from above
(A). &lt;strong&gt;Vcc&lt;/strong&gt; is set at 3.3V. PT used is Everlight PT12-21B/TR8 and IR LED is
IR12-21C/TR8. Rise and Fall times for various &lt;strong&gt;R&lt;/strong&gt; and &lt;strong&gt;RL&lt;/strong&gt; values are given
below. The oscilloscope screens represent the items in the table. Green line
represent the voltage drop across PT and yellow line is the voltage drop across IR
LED. &lt;em&gt;us/div&lt;/em&gt; number represents millisec per division (horizontal). Current
across PT is not calculated since it is a much smaller value.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;strong&gt;R&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;IR LED Current&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;RL&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Rise Time&lt;/strong&gt;&lt;/th&gt;
      &lt;th&gt;&lt;strong&gt;Fall+Storage Time&lt;/strong&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1k ohm&lt;/td&gt;
      &lt;td&gt;2.3 mA&lt;/td&gt;
      &lt;td&gt;30k ohm&lt;/td&gt;
      &lt;td&gt;~250 us&lt;/td&gt;
      &lt;td&gt;~300 us&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;300 ohm&lt;/td&gt;
      &lt;td&gt;7.5 mA&lt;/td&gt;
      &lt;td&gt;4.5k ohm&lt;/td&gt;
      &lt;td&gt;~100 us&lt;/td&gt;
      &lt;td&gt;~100 us&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;100 ohm&lt;/td&gt;
      &lt;td&gt;20.2 mA&lt;/td&gt;
      &lt;td&gt;1.8k ohm&lt;/td&gt;
      &lt;td&gt;~20 us&lt;/td&gt;
      &lt;td&gt;~20 us&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;img src=&quot;/assets/rtime1.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;
&lt;img src=&quot;/assets/rtime2.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;
&lt;img src=&quot;/assets/rtime3.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the above pictures voltage values are inverted. ‘Rise’ is when yellow
line goes from high to low, and vice versa. Rise and fall times are
approximations eye-balled from oscilloscope plots.&lt;/p&gt;

&lt;p&gt;It may appear execessive that at least 20mA current is needed to achieve rise
time of 20us. But with minor modifications to the circuit it is possible to
achieve 1us rise/fall time expending only a few mA current. Using photodiodes and
OpAmps is not necessary.&lt;/p&gt;

&lt;h2 id=&quot;optical-matrix&quot;&gt;Optical Matrix&lt;/h2&gt;

&lt;p&gt;The total number of switches in a full-sized keyboard far exceed the number of
digital pins available in a microcontroller for reading TTL voltage levels. We
can solve this problem two ways: 1) Use a matrix arrangement where a single
GPIO pin is connected to multiple PTs but only one of the PTs is ‘active’ at
any given time, 2) Use a separate device like a multiplexer or shift register
to read multiple PT voltages from a single GPIO pin. In the former case only a
subset of IR LEDs are switched on at any given time (‘scanning’ the matrix). In the
latter case all IR LEDs can be switched on and switch states can all be read at
once. By now you may have already have some ideas about the matrix you want to
design. Following section covers only basic information.&lt;/p&gt;

&lt;p&gt;Optical matrix is not much different from matrix used for mechanical switches.
You either select a column and read rows one by one, or &lt;em&gt;vice versa&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;select-column-and-read-rows&quot;&gt;Select Column and Read Rows&lt;/h3&gt;

&lt;p&gt;In this arrangement we switch on all IR LEDs in a give column and read PT values
across rows. Pseudocode would look like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;initialize GPIO pins

forever do:
    for column in all_columns do:
        select column  /* switch on power to column */
        delay          /* wait for rise time */
        for row in all_rows do:
            read row   /* read state of switch */
        unselect column /* switch off power to column */
        delay          /* wait for storage and fall time */

    compare matrix_state with matrix_state_from_previous_scan
    if above comparison returs true: 
        copy matrix_state to matrix_state_from_previous_scan
        return matrix_state
        
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To power IR LEDs in a column you can use a GPIO pin directly (set as OUTPUT). ARM
based microcontrollers like RP2040 or STM32F4 can handle 20 mA current per pin
(for a maximum of ~40 mA across all pins). If you have 5 columns you can supply
3.5 mA per IR LED (leaving 0.5 mA for PT).&lt;/p&gt;

&lt;p&gt;Reading time is usually negligible compared to PT rise/fall time. A full matrix
scan will take approximately &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(rise_time + fall time) x number_of_columns&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/opic4.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;select-row-and-read-columns&quot;&gt;Select Row and Read Columns&lt;/h3&gt;

&lt;p&gt;In this arrangement we select a row and read column pins one by one. A full
matrix scan will take &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(rise_time + fall_time) x number_of_rows&lt;/code&gt;. This is my
preferred arrangement since we can achieve higher scan rates. Rows can be
powered using a N-Mosfet acting as a low-side switch connected to a GPIO pin at
the gate. We can supply higher power to IR LED since we are not limited by GPIO
pin.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/opic3.png&quot; alt=&quot;image&quot; width=&quot;550&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt;: To minimize power losses IR LEDs could be connected in series when
possible. Modify the above schematic accordingly for your use-case. Voltage
drop across IR LED (Vf) is typically 1.2V-1.3V. Using a 5V supply it is
possible to connect 3 IR LEDs in series and still have headroom for regulation.
LED forward voltage drop has inverse relationship with temperature. As LEDs
heat up the forward voltage reduces resulting in more current. This causes
further increase in temperature and could lead to thermal runaway. It is good
to keep 20%-30% voltage headroom across resistor for proper regulation.&lt;/p&gt;

&lt;h3 id=&quot;optimization&quot;&gt;Optimization&lt;/h3&gt;

&lt;p&gt;One easy way you can increase the performance is by splitting the matrix into
two. We can scan rows from each matrix alternately. This way when one matrix is
waiting in ‘rise’ time, the other is waiting in ‘fall’ time. But storage time
has to be accounted for each row separately. Latency is decreased by the same
amount as rise time.&lt;/p&gt;

&lt;p&gt;Using RP2040 (which has 30 GPIO pins) in a keyboard with 72 switches we can use
2 4x9 matrices, and we need 26 GPIO pins. Each row has 9 IR LEDs and each can be
supplied with at least 20mA.&lt;/p&gt;

&lt;p&gt;FS (full speed) USB supports 1 ms polling (1 kHz) while HS (high speed) USB
supports 125us polling (8 kHz). Even though phototransistors are inherently
slow it is possible to achieve ~8 kHz scan rate using PT without additional
circuitry, while photodiodes in combination with OpAmps can generate square
waves upto 100 kHz. Hopefully, this writeup kindled some curiousity in you
about the workings of optical keyboards.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;optical-keyboards&quot;&gt;Optical Keyboards&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/girishji/optical-keyboard-mx&quot;&gt;Optical keyboard with MX switches&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/girishji/keychron-optical-keyboard&quot;&gt;Optical keyboard with low profile switches&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/girishji/optical-amoeba&quot;&gt;Amoeba single switch pcb&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;questions&quot;&gt;Questions?&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://discord.gg/FafPTRDC&quot;&gt;Optical Future&lt;/a&gt;&lt;/p&gt;</content><author><name>{&quot;name&quot;=&gt;nil, &quot;email&quot;=&gt;&quot;&quot;}</name></author><category term="optical" /><category term="keyboard" /><category term="switch" /><summary type="html">This guide will walk you through some concepts that will help you design a basic optical keyboard PCB. No special knowledge is assumed beyond engineering basics. The circuits used here are rudimentary and they will provide performance comparable to a mechanical keyboard. Low latency specialized circuits are not covered in this guide.</summary></entry></feed>