1# This program is free software; you can redistribute it and/or modify 2# it under the terms of the GNU General Public License as published by 3# the Free Software Foundation; either version 2 of the License, or 4# (at your option) any later version. 5# 6# This program is distributed in the hope that it will be useful, 7# but WITHOUT ANY WARRANTY; without even the implied warranty of 8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9# GNU General Public License for more details. 10# 11# You should have received a copy of the GNU General Public License 12# along with this program; if not, write to the Free Software 13# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 14# MA 02110-1301, USA. 15 16# last change: 2021, Jul 19. 17 18import pcbnew 19import FootprintWizardBase 20 21# Additional import for QRCode 22# see https://github.com/kazuhikoarase/qrcode-generator/blob/master/python/qrcode.py 23import kicad_qrcode as qrcode # TODO: local qrcode package is preferred, so we renamed it 24 25class QRCodeWizard(FootprintWizardBase.FootprintWizard): 26 GetName = lambda self: '2D Barcode QRCode' 27 GetDescription = lambda self: 'QR Code barcode generator' 28 GetReferencePrefix = lambda self: 'QR***' 29 GetValue = lambda self: self.module.Value().GetText() 30 31 def GenerateParameterList(self): 32 self.AddParam("Barcode", "Qr Pixel Width", self.uMM, 0.5, min_value=0.4) 33 self.AddParam("Barcode", "Border Margin (Px)", self.uInteger, 0) 34 self.AddParam("Barcode", "Contents", self.uString, 'Example') 35 self.AddParam("Barcode", "Negative", self.uBool, False) 36 self.AddParam("Barcode", "Use SilkS layer", self.uBool, False) 37 self.AddParam("Barcode", "Use Cu layer", self.uBool, True) 38 self.AddParam("Caption", "Enabled", self.uBool, True) 39 self.AddParam("Caption", "Height", self.uMM, 1.0) 40 self.AddParam("Caption", "Width", self.uMM, 1.0) 41 self.AddParam("Caption", "Thickness", self.uMM, 0.15) 42 43 44 def CheckParameters(self): 45 # 512 bits maximum in this type of QR code with 2 bytes reserved 46 self.Barcode = str(self.parameters['Barcode']['Contents'])[:61] 47 self.X = self.parameters['Barcode']['Qr Pixel Width'] 48 self.negative = self.parameters['Barcode']['Negative'] 49 self.UseSilkS = self.parameters['Barcode']['Use SilkS layer'] 50 self.UseCu = self.parameters['Barcode']['Use Cu layer'] 51 self.border = int(self.parameters['Barcode']['Border Margin (Px)']) 52 self.textHeight = int(self.parameters['Caption']['Height']) 53 self.textThickness = int(self.parameters['Caption']['Thickness']) 54 self.textWidth = int(self.parameters['Caption']['Width']) 55 self.module.Value().SetText(str(self.Barcode)) 56 57 if self.border < 0: 58 self.border = 0 59 60 # Build Qrcode 61 self.qr = qrcode.QRCode() 62 self.qr.setTypeNumber(4) 63 # ErrorCorrectLevel: L = 7%, M = 15% Q = 25% H = 30% 64 self.qr.setErrorCorrectLevel(qrcode.ErrorCorrectLevel.M) 65 self.qr.addData(str(self.Barcode)) 66 self.qr.make() 67 68 def drawPixelSquareArea( self, layer, size, xposition, yposition): 69 # creates a FP_SHAPE of rectangle type. The rectangle is square 70 rectangle = pcbnew.FP_SHAPE(self.module) 71 rectangle.SetShape(pcbnew.S_RECT) 72 rectangle.SetWidth( 0 ) 73 rectangle.SetFilled( True ) 74 rectangle.SetLayer(layer) 75 halfsize = int(size/2) 76 rectangle.SetStartX( -halfsize+xposition ) 77 rectangle.SetStartY( -halfsize+yposition ) 78 rectangle.SetEndX( halfsize+xposition ) 79 rectangle.SetEndY( halfsize+yposition ) 80 rectangle.SetStart0( rectangle.GetStart() ) 81 rectangle.SetEnd0( rectangle.GetEnd() ) 82 return rectangle 83 84 85 def _drawQrPixel(self, xposition, yposition): 86 # build a rectangular pad as a dot on copper layer, 87 # and a rectangle (a square) on silkscreen 88 if self.UseCu: 89 pad = pcbnew.PAD(self.module) 90 pad.SetSize(pcbnew.wxSize(self.X, self.X)) 91 pad_pos = pcbnew.wxPoint(xposition,yposition) 92 pad.SetPosition(pad_pos) 93 pad.SetPos0(pad_pos) 94 pad.SetShape(pcbnew.PAD_SHAPE_RECT) 95 pad.SetAttribute(pcbnew.PAD_ATTRIB_SMD) 96 pad.SetName("") 97 layerset = pcbnew.LSET() 98 layerset.AddLayer(pcbnew.F_Cu) 99 pad.SetLayerSet( layerset ) 100 self.module.Add(pad) 101 if self.UseSilkS: 102 rectangle=self.drawPixelSquareArea(pcbnew.F_SilkS, self.X, xposition, yposition) 103 self.module.Add(rectangle) 104 105 106 def BuildThisFootprint(self): 107 if self.border >= 0: 108 # Adding border: Create a new array larger than the self.qr.modules 109 sz = self.qr.modules.__len__() + (self.border * 2) 110 arrayToDraw = [ [ 0 for a in range(sz) ] for b in range(sz) ] 111 lineposition = self.border 112 113 for i in self.qr.modules: 114 columnposition = self.border 115 for j in i: 116 arrayToDraw[lineposition][columnposition] = j 117 columnposition += 1 118 lineposition += 1 119 else: 120 # No border: using array as is 121 arrayToDraw = self.qr.modules 122 123 # used many times... 124 half_number_of_elements = arrayToDraw.__len__() / 2 125 area_width = arrayToDraw.__len__()*self.X + self.border*2 126 127 # Center position of QrCode 128 yposition = - int(half_number_of_elements * self.X) + int(self.X/2) 129 area_height = arrayToDraw.__len__()*self.X + self.border*2 130 131 for line in arrayToDraw: 132 xposition = - int(half_number_of_elements * self.X) + int(self.X/2) 133 for pixel in line: 134 # Trust table for drawing a pixel 135 # Negative is a boolean; 136 # each pixel is a boolean (need to draw of not) 137 # Negative | Pixel | Result 138 # 0 | 0 | 0 139 # 0 | 1 | 1 140 # 1 | 0 | 1 141 # 1 | 1 | 0 142 # => Draw as Xor 143 if self.negative != pixel: # Xor... 144 self._drawQrPixel(xposition, yposition) 145 xposition += self.X 146 yposition += self.X 147 148 # Add value field 149 textPosition = int((self.textHeight) + ((1 + half_number_of_elements) * self.X)) 150 pos = pcbnew.wxPoint(0, - textPosition) 151 self.module.Value().SetPosition(pos) 152 self.module.Value().SetPos0(pos) 153 self.module.Value().SetTextHeight(self.textHeight) 154 self.module.Value().SetTextWidth(self.textWidth) 155 self.module.Value().SetTextThickness(self.textThickness) 156 157 # Add Reference field 158 pos = pcbnew.wxPoint(0, textPosition) 159 self.module.Reference().SetPosition(pos) 160 self.module.Reference().SetPos0(pos) 161 self.module.Reference().SetTextHeight(self.textHeight) 162 self.module.Reference().SetTextWidth(self.textWidth) 163 self.module.Reference().SetTextThickness(self.textThickness) 164 self.module.Value().SetLayer(pcbnew.F_SilkS) 165 166 #build the footprint courtyard 167 self.draw.SetLayer( pcbnew.F_CrtYd ) 168 self.draw.SetLineThickness( pcbnew.FromMM( 0.05 ) ) #Default per KLC F5.1 as of 12/2018 169 cr_margin = pcbnew.FromMM( 0.1 ) 170 self.draw.Box(0, 0, area_width + cr_margin*2, area_height + cr_margin*2) 171 172 #build the footprint solder mask: the solder mask covers all copper pads 173 if self.UseCu: 174 self.draw.SetLineThickness( 0 ) 175 sm_margin = pcbnew.FromMM( 0.05 ) 176 rectangle=self.drawPixelSquareArea(pcbnew.F_Mask, area_width + sm_margin*2, 0, 0) 177 self.module.Add(rectangle) 178 179 180QRCodeWizard().register() 181