├── LICENSE ├── README.md ├── hcsr.go ├── lcd_hd44780 ├── README.md └── lcd_hd44780_4bit.go ├── mcp3008 ├── README.md └── driver.go ├── rf522 ├── README.md ├── Rf522.go ├── Rf522_test.go ├── app │ └── rf522_main.go └── commands │ ├── PICC.go │ └── Registers.go ├── rf522_main ├── sensor_hcsr04 ├── README.md └── sensor_hcsr04.go └── ssd1306 ├── Constants.go ├── OLEDInterface.go ├── oled_i2c └── OLEDControl.go └── oled_spi └── OLEDControl.go /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Golang-RPi-Extras 2 | Go interfaces to 3 | * Hitachi HD44780 LCD controller [HD44780](lcd_hd44780) 4 | * Ultrasonic Ranging Module [HC-SR04](sensor_hcsr04) 5 | * Ultrasonic Ranging Module [MCP3008](mcp3008) -------------------------------------------------------------------------------- /hcsr.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | lcd "github.com/jdevelop/golang-rpi-extras/lcd_hd44780" 6 | hc "github.com/jdevelop/golang-rpi-extras/sensor_hcsr04" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | 12 | h := hc.NewHCSR04(4, 25) 13 | 14 | myLcd, err := lcd.NewLCD4([]int{27, 22, 23, 24}, 17, 18) 15 | 16 | if err != nil { 17 | panic(err.Error()) 18 | } 19 | 20 | myLcd.Init() 21 | myLcd.Cls() 22 | 23 | for true { 24 | distance := h.MeasureDistance() 25 | fmt.Println(distance) 26 | myLcd.SetCursor(0, 0) 27 | myLcd.Print(fmt.Sprintf("Distance: %5.2f", distance)) 28 | time.Sleep(time.Duration(1) * time.Second) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lcd_hd44780/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ```go 4 | package main 5 | 6 | import lcd "github.com/jdevelop/golang-rpi-extras/lcd_hd44780" 7 | 8 | func main() { 9 | // Use BCM PIN numbering 10 | // 4 pins for data transfer 11 | // RS pin 12 | // E pin 13 | rpi, err := lcd.NewLCD4([]int{27, 22, 23, 24}, 17, 18) 14 | 15 | if err != nil { 16 | panic(err.Error()) 17 | } 18 | 19 | rpi.Cls() 20 | rpi.Print("-= HELLO =-") 21 | rpi.SetCursor(1, 0) 22 | rpi.Print("-= WORLD =-") 23 | 24 | } 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /lcd_hd44780/lcd_hd44780_4bit.go: -------------------------------------------------------------------------------- 1 | package lcd_hd44780 2 | 3 | import ( 4 | "github.com/stianeikeland/go-rpio" 5 | "time" 6 | ) 7 | 8 | const LineTwo = 0x40 // start of line 2 9 | 10 | type PiLCD4 struct { 11 | // Data pins 12 | dataPins []rpio.Pin 13 | 14 | // register select pin 15 | rsPin rpio.Pin 16 | 17 | // enable pin 18 | enablePin rpio.Pin 19 | } 20 | 21 | func NewLCD4(data []int, rs int, e int) (pLcd PiLCD4, err error) { 22 | if err = rpio.Open(); err != nil { 23 | return 24 | } 25 | pLcd.rsPin = rpio.Pin(rs) 26 | pLcd.enablePin = rpio.Pin(e) 27 | pLcd.dataPins = make([]rpio.Pin, len(data)) 28 | for i, v := range data { 29 | pLcd.dataPins[i] = rpio.Pin(v) 30 | } 31 | return 32 | } 33 | 34 | type PiLCD interface { 35 | Init() 36 | 37 | Cls() 38 | 39 | Print(data string) 40 | 41 | WriteChar(data uint8) 42 | 43 | SetCursor(line uint8, column uint8) 44 | } 45 | 46 | func clearBits(r *PiLCD4) { 47 | for _, v := range r.dataPins { 48 | v.Low() 49 | } 50 | } 51 | 52 | func (r *PiLCD4) Init() { 53 | 54 | r.rsPin.Output() 55 | r.enablePin.Output() 56 | 57 | for _, v := range r.dataPins { 58 | v.Output() 59 | } 60 | 61 | clearBits(r) 62 | 63 | delayMs(15) 64 | 65 | r.rsPin.Low() // set RS to low 66 | r.enablePin.Low() // set E to low 67 | 68 | writeDelay := func(data uint8, delay int) { 69 | write4Bits(r, data) 70 | delayUs(delay) 71 | } 72 | 73 | // repeat the Init command 3 times 74 | writeDelay(3, 50) 75 | writeDelay(3, 10) 76 | writeDelay(3, 10) 77 | 78 | writeDelay(2, 10) 79 | 80 | // set 4bit mode. 2 lines, 5x7 81 | writeInstruction(r, 0x14) 82 | 83 | // disable display 84 | writeInstruction(r, 0x10) 85 | 86 | // clear display 87 | writeInstruction(r, 0x1) 88 | delayMs(2) 89 | 90 | // cursor shift right, no display move 91 | writeInstruction(r, 0x06) 92 | 93 | // enable display no cursor 94 | writeInstruction(r, 0x0c) 95 | 96 | // clear screen 97 | writeInstruction(r, 0x1) 98 | delayMs(2) 99 | 100 | // home 101 | writeInstruction(r, 2) 102 | delayMs(2) 103 | } 104 | 105 | func (r *PiLCD4) Cls() { 106 | writeInstruction(r, 0x01) 107 | delayMs(2) 108 | } 109 | 110 | func (r *PiLCD4) SetCursor(line uint8, column uint8) { 111 | writeInstruction(r, 0x80|(line*LineTwo+column)) 112 | } 113 | 114 | func (r *PiLCD4) Print(data string) { 115 | for _, v := range []byte(data) { 116 | r.WriteChar(v) 117 | } 118 | } 119 | 120 | func (r *PiLCD4) WriteChar(data uint8) { 121 | sendData(r) 122 | write4Bits(r, data>>4) 123 | write4Bits(r, data) 124 | delayUs(10) 125 | } 126 | 127 | // =============================================== service methods ============================================ 128 | 129 | func write4Bits(ref *PiLCD4, data uint8) { 130 | for i, v := range ref.dataPins { 131 | if data&(1< 0 { 132 | v.High() 133 | } else { 134 | v.Low() 135 | } 136 | } 137 | strobe(ref) 138 | } 139 | 140 | func sendInstruction(ref *PiLCD4) { 141 | ref.rsPin.Low() 142 | ref.enablePin.Low() 143 | } 144 | 145 | func sendData(ref *PiLCD4) { 146 | ref.rsPin.High() 147 | ref.enablePin.Low() 148 | } 149 | 150 | func writeInstruction(ref *PiLCD4, data uint8) { 151 | sendInstruction(ref) 152 | // write high 4 bits 153 | write4Bits(ref, data>>4) 154 | // write low bits 155 | write4Bits(ref, data) 156 | delayUs(50) 157 | } 158 | 159 | func strobe(ref *PiLCD4) { 160 | ref.enablePin.High() 161 | delayUs(2) 162 | ref.enablePin.Low() 163 | } 164 | 165 | func delayUs(ms int) { 166 | time.Sleep(time.Duration(ms) * time.Microsecond) 167 | } 168 | 169 | func delayMs(ms int) { 170 | time.Sleep(time.Duration(ms) * time.Millisecond) 171 | } 172 | -------------------------------------------------------------------------------- /mcp3008/README.md: -------------------------------------------------------------------------------- 1 | # Read value from ADT [MCP3008](https://www.adafruit.com/products/856) 2 | 3 | # Example 4 | 5 | ```go 6 | package main 7 | 8 | import ( 9 | "time" 10 | "fmt" 11 | mcp "github.com/jdevelop/golang-rpi-extras/mcp3008" 12 | ) 13 | 14 | func main() { 15 | 16 | const channel = 0 17 | 18 | // /dev/spidev0.0 19 | mcp, err := mcp.NewMCP3008(0, 0, mcp.Mode0, 500000) 20 | 21 | if err != nil { 22 | panic(err.Error()) 23 | } 24 | 25 | for { 26 | fmt.Println(mcp.Measure(channel)) 27 | time.Sleep(time.Second) 28 | } 29 | 30 | } 31 | ``` -------------------------------------------------------------------------------- /mcp3008/driver.go: -------------------------------------------------------------------------------- 1 | package mcp3008 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/exp/io/spi" 6 | ) 7 | 8 | type Mode int 9 | 10 | const ( 11 | Mode0 = Mode(0) 12 | Mode1 = Mode(1) 13 | Mode2 = Mode(2) 14 | Mode3 = Mode(3) 15 | ) 16 | 17 | type MCP3008 struct { 18 | bus int 19 | channel int 20 | dev *spi.Device 21 | } 22 | 23 | type MeasureAnalog interface { 24 | Measure(channel int) (result int, err error) 25 | } 26 | 27 | func NewMCP3008(bus int, busChannel int, mode Mode, maxSpeed int64) (mcp MCP3008, resErr error) { 28 | var dev, err = spi.Open(&spi.Devfs{ 29 | Dev: fmt.Sprintf("/dev/spidev%d.%d", bus, busChannel), 30 | Mode: spi.Mode(mode), 31 | MaxSpeed: maxSpeed, 32 | }) 33 | if err != nil { 34 | resErr = err 35 | return 36 | } 37 | mcp.dev = dev 38 | return 39 | } 40 | 41 | func (r MCP3008) Measure(channel int) (result int, err error) { 42 | resp := make([]byte, 3) 43 | if err := r.dev.Tx([]byte{1, byte((8 + channel) << 4), 0}, resp); err == nil { 44 | result = int(resp[1]&3)<<8 + int(resp[2]) 45 | } 46 | return 47 | } 48 | -------------------------------------------------------------------------------- /rf522/README.md: -------------------------------------------------------------------------------- 1 | # RF522 wireless RFID reader 2 | 3 | Ported the [Python](https://github.com/mxgxw/MFRC522-python) library to access RF522 cards to golang. 4 | 5 | Example usage: 6 | 7 | ```go 8 | package main 9 | 10 | import ( 11 | "github.com/jdevelop/golang-rpi-extras/rf522" 12 | "log" 13 | "github.com/sirupsen/logrus" 14 | "os" 15 | "flag" 16 | "fmt" 17 | "strconv" 18 | "github.com/jdevelop/golang-rpi-extras/rf522/commands" 19 | ) 20 | 21 | func main() { 22 | 23 | //currentAccessKey := [6]byte{1, 2, 3, 4, 5, 6} 24 | currentAccessKey := [6]byte{6, 5, 4, 3, 2, 1} 25 | currentAccessMethod := byte(commands.PICC_AUTHENT1B) 26 | 27 | sector := flag.Int("s", 1, "card sector") 28 | block := flag.Int("b", 0, "card block") 29 | overwriteKey := flag.Bool("wa", false, "Overwrite keys") 30 | overwriteBlock := flag.Bool("wb", false, "Overwrite block with 0-15") 31 | 32 | flag.Parse() 33 | 34 | // use BCM numbering here 35 | logrus.SetLevel(logrus.InfoLevel) 36 | log.SetOutput(os.Stdout) 37 | rfid, err := rf522.MakeRFID(0, 0, 1000000, 25, 24) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | 42 | data, err := rfid.ReadCard(currentAccessMethod, *sector, *block, currentAccessKey[:]) 43 | auth, err := rfid.ReadAuth(currentAccessMethod, *sector, currentAccessKey[:]) 44 | 45 | access := rf522.ParseBlockAccess(auth[6:10]) 46 | 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | fmt.Printf("RFID sector %d, block %d : %v, auth: %v\n", *sector, *block, data, auth) 52 | fmt.Printf("Permissions: B0: %s, B1: %s, B2: %s, B3/A: %s\n", 53 | strconv.FormatUint(uint64(access.B0), 2), 54 | strconv.FormatUint(uint64(access.B1), 2), 55 | strconv.FormatUint(uint64(access.B2), 2), 56 | strconv.FormatUint(uint64(access.B3), 2), 57 | ) 58 | 59 | if *overwriteBlock { 60 | err = rfid.WriteBlock(currentAccessMethod, 61 | *sector, 62 | *block, 63 | [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 64 | currentAccessKey[:]) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | } 69 | 70 | if *overwriteKey { 71 | err = rfid.WriteSectorTrail(commands.PICC_AUTHENT1A, 72 | *sector, 73 | [6]byte{1, 2, 3, 4, 5, 6}, 74 | [6]byte{6, 5, 4, 3, 2, 1}, 75 | &rf522.BlocksAccess{ 76 | B0: rf522.RAB_WB_IB_DAB, 77 | B1: rf522.RB_WB_IN_DN, 78 | B2: rf522.AnyKeyRWID, 79 | B3: rf522.KeyA_RN_WN_BITS_RAB_WN_KeyB_RN_WN, 80 | }, 81 | currentAccessKey[:], 82 | ) 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | fmt.Println("Write successful") 87 | } 88 | 89 | } 90 | ``` -------------------------------------------------------------------------------- /rf522/Rf522.go: -------------------------------------------------------------------------------- 1 | package rf522 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/ecc1/spi" 9 | "github.com/jdevelop/golang-rpi-extras/rf522/commands" 10 | "github.com/jdevelop/gpio" 11 | rpio "github.com/jdevelop/gpio/rpi" 12 | "github.com/sirupsen/logrus" 13 | ) 14 | 15 | type RFID struct { 16 | ResetPin gpio.Pin 17 | IrqPin gpio.Pin 18 | Authenticated bool 19 | antennaGain int 20 | MaxSpeedHz int 21 | spiDev *spi.Device 22 | stop chan interface{} 23 | } 24 | 25 | var DefaultKey = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} 26 | 27 | func MakeRFID(busId, deviceId, maxSpeed, resetPin, irqPin int) (device *RFID, err error) { 28 | 29 | spiDev, err := spi.Open(fmt.Sprintf("/dev/spidev%d.%d", busId, deviceId), maxSpeed, 0) 30 | 31 | if err != nil { 32 | return 33 | } 34 | 35 | err = spiDev.SetLSBFirst(false) 36 | if err != nil { 37 | spiDev.Close() 38 | return 39 | } 40 | 41 | err = spiDev.SetBitsPerWord(8) 42 | 43 | if err != nil { 44 | spiDev.Close() 45 | return 46 | } 47 | 48 | dev := &RFID{ 49 | spiDev: spiDev, 50 | MaxSpeedHz: maxSpeed, 51 | antennaGain: 4, 52 | stop: make(chan interface{}, 1), 53 | } 54 | 55 | pin, err := rpio.OpenPin(resetPin, gpio.ModeOutput) 56 | if err != nil { 57 | spiDev.Close() 58 | return 59 | } 60 | dev.ResetPin = pin 61 | dev.ResetPin.Set() 62 | 63 | pin, err = rpio.OpenPin(irqPin, gpio.ModeInput) 64 | if err != nil { 65 | spiDev.Close() 66 | return 67 | } 68 | dev.IrqPin = pin 69 | dev.IrqPin.PullUp() 70 | 71 | err = dev.Init() 72 | 73 | device = dev 74 | 75 | return 76 | } 77 | 78 | func (r *RFID) Init() (err error) { 79 | err = r.Reset() 80 | if err != nil { 81 | return 82 | } 83 | err = r.devWrite(0x2A, 0x8D) 84 | if err != nil { 85 | return 86 | } 87 | err = r.devWrite(0x2B, 0x3E) 88 | if err != nil { 89 | return 90 | } 91 | err = r.devWrite(0x2D, 30) 92 | if err != nil { 93 | return 94 | } 95 | err = r.devWrite(0x2C, 0) 96 | if err != nil { 97 | return 98 | } 99 | err = r.devWrite(0x15, 0x40) 100 | if err != nil { 101 | return 102 | } 103 | err = r.devWrite(0x11, 0x3D) 104 | if err != nil { 105 | return 106 | } 107 | err = r.devWrite(0x26, byte(r.antennaGain)<<4) 108 | if err != nil { 109 | return 110 | } 111 | err = r.SetAntenna(true) 112 | if err != nil { 113 | return 114 | } 115 | logrus.Debug("Init done") 116 | return 117 | } 118 | 119 | func (r *RFID) Close() error { 120 | r.stop <- true 121 | close(r.stop) 122 | return r.spiDev.Close() 123 | } 124 | 125 | func (r *RFID) writeSpiData(dataIn []byte) (out []byte, err error) { 126 | out = make([]byte, len(dataIn)) 127 | copy(out, dataIn) 128 | err = r.spiDev.Transfer(out) 129 | return 130 | } 131 | 132 | func printBytes(data []byte) (res string) { 133 | res = "[" 134 | for _, v := range data[0 : len(data)-1] { 135 | res = res + fmt.Sprintf("%02x, ", byte(v)) 136 | } 137 | res = res + fmt.Sprintf("%02x", data[len(data)-1]) 138 | res = res + "]" 139 | return 140 | } 141 | 142 | /* 143 | def dev_write(self, address, value): 144 | self.spi_transfer([(address << 1) & 0x7E, value]) 145 | */ 146 | 147 | func (r *RFID) devWrite(address int, data byte) (err error) { 148 | newData := [2]byte{(byte(address) << 1) & 0x7E, data} 149 | readBuf, err := r.writeSpiData(newData[:]) 150 | if logrus.GetLevel() == logrus.DebugLevel { 151 | newData[0] = newData[0] >> 1 152 | logrus.Debug(">>" + printBytes(newData[:]) + " " + printBytes(readBuf)) 153 | } 154 | return 155 | } 156 | 157 | /* 158 | def dev_read(self, address): 159 | return self.spi_transfer([((address << 1) & 0x7E) | 0x80, 0])[1] 160 | */ 161 | func (r *RFID) devRead(address int) (result byte, err error) { 162 | data := [2]byte{((byte(address) << 1) & 0x7E) | 0x80, 0} 163 | rb, err := r.writeSpiData(data[:]) 164 | if logrus.GetLevel() == logrus.DebugLevel { 165 | data[0] = (data[0] >> 1) & 0x7f 166 | logrus.Debug("<<" + printBytes(data[:]) + " " + printBytes(rb)) 167 | } 168 | result = rb[1] 169 | return 170 | } 171 | 172 | /* 173 | def set_bitmask(self, address, mask): 174 | current = self.dev_read(address) 175 | self.dev_write(address, current | mask) 176 | */ 177 | func (r *RFID) setBitmask(address, mask int) (err error) { 178 | logrus.Debug("Set mask ", address, mask) 179 | current, err := r.devRead(address) 180 | if err != nil { 181 | return 182 | } 183 | logrus.Debug("Set mask value ", address, current|byte(mask)) 184 | err = r.devWrite(address, current|byte(mask)) 185 | return 186 | } 187 | 188 | /* 189 | def clear_bitmask(self, address, mask): 190 | current = self.dev_read(address) 191 | self.dev_write(address, current & (~mask)) 192 | */ 193 | func (r *RFID) clearBitmask(address, mask int) (err error) { 194 | logrus.Debug("Clear mask ", address, mask) 195 | current, err := r.devRead(address) 196 | if err != nil { 197 | return 198 | } 199 | logrus.Debug("Set mask value ", address, current&^byte(mask)) 200 | err = r.devWrite(address, current&^byte(mask)) 201 | return 202 | 203 | } 204 | 205 | /* 206 | def set_antenna_gain(self, gain): 207 | """ 208 | Sets antenna gain from a value from 0 to 7. 209 | """ 210 | if 0 <= gain <= 7: 211 | self.antenna_gain = gain 212 | */ 213 | func (r *RFID) SetAntennaGain(gain int) { 214 | if 0 <= gain && gain <= 7 { 215 | r.antennaGain = gain 216 | } 217 | } 218 | 219 | func (r *RFID) Reset() (err error) { 220 | r.Authenticated = false 221 | err = r.devWrite(commands.CommandReg, commands.PCD_RESETPHASE) 222 | return 223 | } 224 | 225 | /* 226 | def set_antenna(self, state): 227 | if state == True: 228 | current = self.dev_read(self.reg_tx_control) 229 | if ~(current & 0x03): 230 | self.set_bitmask(self.reg_tx_control, 0x03) 231 | else: 232 | self.clear_bitmask(self.reg_tx_control, 0x03) 233 | 234 | */ 235 | func (r *RFID) SetAntenna(state bool) (err error) { 236 | if state { 237 | current, err := r.devRead(commands.TxControlReg) 238 | logrus.Debug("Antenna", current) 239 | if err != nil { 240 | return err 241 | } 242 | if current&0x03 == 0 { 243 | err = r.setBitmask(commands.TxControlReg, 0x03) 244 | } 245 | } else { 246 | err = r.clearBitmask(commands.TxControlReg, 0x03) 247 | } 248 | return 249 | } 250 | 251 | func (r *RFID) cardWrite(command byte, data []byte) (backData []byte, backLength int, err error) { 252 | backData = make([]byte, 0) 253 | backLength = -1 254 | irqEn := byte(0x00) 255 | irqWait := byte(0x00) 256 | 257 | switch command { 258 | case commands.PCD_AUTHENT: 259 | irqEn = 0x12 260 | irqWait = 0x10 261 | case commands.PCD_TRANSCEIVE: 262 | irqEn = 0x77 263 | irqWait = 0x30 264 | } 265 | 266 | r.devWrite(commands.CommIEnReg, irqEn|0x80) 267 | r.clearBitmask(commands.CommIrqReg, 0x80) 268 | r.setBitmask(commands.FIFOLevelReg, 0x80) 269 | r.devWrite(commands.CommandReg, commands.PCD_IDLE) 270 | 271 | for _, v := range data { 272 | r.devWrite(commands.FIFODataReg, v) 273 | } 274 | 275 | r.devWrite(commands.CommandReg, command) 276 | 277 | if command == commands.PCD_TRANSCEIVE { 278 | r.setBitmask(commands.BitFramingReg, 0x80) 279 | } 280 | 281 | i := 2000 282 | n := byte(0) 283 | 284 | for ; i > 0; i-- { 285 | n, err = r.devRead(commands.CommIrqReg) 286 | if err != nil { 287 | return 288 | } 289 | if n&(irqWait|1) != 0 { 290 | break 291 | } 292 | } 293 | 294 | r.clearBitmask(commands.BitFramingReg, 0x80) 295 | 296 | if i == 0 { 297 | err = errors.New("can't read data after 2000 loops") 298 | return 299 | } 300 | 301 | if d, err1 := r.devRead(commands.ErrorReg); err1 != nil || d&0x1B != 0 { 302 | err = err1 303 | logrus.Error("E2") 304 | return 305 | } 306 | 307 | if n&irqEn&0x01 == 1 { 308 | logrus.Error("E1") 309 | err = errors.New("IRQ error") 310 | return 311 | } 312 | 313 | if command == commands.PCD_TRANSCEIVE { 314 | n, err = r.devRead(commands.FIFOLevelReg) 315 | if err != nil { 316 | return 317 | } 318 | lastBits, err1 := r.devRead(commands.ControlReg) 319 | if err1 != nil { 320 | err = err1 321 | return 322 | } 323 | lastBits = lastBits & 0x07 324 | if lastBits != 0 { 325 | backLength = (int(n)-1)*8 + int(lastBits) 326 | } else { 327 | backLength = int(n) * 8 328 | } 329 | 330 | if n == 0 { 331 | n = 1 332 | } 333 | 334 | if n > 16 { 335 | n = 16 336 | } 337 | 338 | for i := byte(0); i < n; i++ { 339 | byteVal, err1 := r.devRead(commands.FIFODataReg) 340 | if err1 != nil { 341 | err = err1 342 | return 343 | } 344 | backData = append(backData, byteVal) 345 | } 346 | 347 | } 348 | 349 | return 350 | } 351 | 352 | func (r *RFID) Request() (backBits int, err error) { 353 | backBits = 0 354 | err = r.devWrite(commands.BitFramingReg, 0x07) 355 | if err != nil { 356 | return 357 | } 358 | 359 | _, backBits, err = r.cardWrite(commands.PCD_TRANSCEIVE, []byte{0x26}[:]) 360 | 361 | logrus.Info(err, backBits) 362 | 363 | if backBits != 0x10 { 364 | err = errors.New(fmt.Sprintf("wrong number of bits %d", backBits)) 365 | } 366 | 367 | return 368 | } 369 | 370 | func (r *RFID) Wait() (err error) { 371 | irqChannel := make(chan bool) 372 | r.IrqPin.BeginWatch(gpio.EdgeFalling, func() { 373 | defer func() { 374 | if recover() != nil { 375 | err = errors.New("panic") 376 | } 377 | }() 378 | irqChannel <- true 379 | }) 380 | 381 | defer func() { 382 | r.IrqPin.EndWatch() 383 | close(irqChannel) 384 | }() 385 | 386 | err = r.Init() 387 | if err != nil { 388 | return 389 | } 390 | err = r.devWrite(commands.CommIrqReg, 0x00) 391 | if err != nil { 392 | return 393 | } 394 | err = r.devWrite(commands.CommIEnReg, 0xA0) 395 | if err != nil { 396 | return 397 | } 398 | logrus.SetLevel(logrus.ErrorLevel) 399 | 400 | interruptLoop: 401 | for { 402 | err = r.devWrite(commands.FIFODataReg, 0x26) 403 | if err != nil { 404 | return 405 | } 406 | err = r.devWrite(commands.CommandReg, 0x0C) 407 | if err != nil { 408 | return 409 | } 410 | err = r.devWrite(commands.BitFramingReg, 0x87) 411 | if err != nil { 412 | return 413 | } 414 | select { 415 | case <-r.stop: 416 | return errors.New("stop signal") 417 | case _ = <-irqChannel: 418 | break interruptLoop 419 | case <-time.After(100 * time.Millisecond): 420 | // do nothing 421 | } 422 | } 423 | return 424 | } 425 | 426 | func (r *RFID) AntiColl() (backData []byte, err error) { 427 | 428 | err = r.devWrite(commands.BitFramingReg, 0x00) 429 | 430 | backData, _, err = r.cardWrite(commands.PCD_TRANSCEIVE, []byte{commands.PICC_ANTICOLL, 0x20}[:]) 431 | 432 | if err != nil { 433 | logrus.Error("Card write ", err) 434 | return 435 | } 436 | 437 | if len(backData) != 5 { 438 | err = errors.New(fmt.Sprintf("Back data expected 5, actual %d", len(backData))) 439 | return 440 | } 441 | 442 | crc := byte(0) 443 | 444 | for _, v := range backData[:4] { 445 | crc = crc ^ v 446 | } 447 | 448 | logrus.Debug("Back data ", printBytes(backData), ", CRC ", printBytes([]byte{crc})) 449 | 450 | if crc != backData[4] { 451 | err = errors.New(fmt.Sprintf("CRC mismatch, expected %02x actual %02x", crc, backData[4])) 452 | } 453 | 454 | return 455 | } 456 | 457 | func (r *RFID) CRC(inData []byte) (res []byte, err error) { 458 | res = []byte{0, 0} 459 | err = r.clearBitmask(commands.DivIrqReg, 0x04) 460 | if err != nil { 461 | return 462 | } 463 | err = r.setBitmask(commands.FIFOLevelReg, 0x80) 464 | if err != nil { 465 | return 466 | } 467 | for _, v := range inData { 468 | r.devWrite(commands.FIFODataReg, v) 469 | } 470 | err = r.devWrite(commands.CommandReg, commands.PCD_CALCCRC) 471 | if err != nil { 472 | return 473 | } 474 | for i := byte(0xFF); i > 0; i-- { 475 | n, err1 := r.devRead(commands.DivIrqReg) 476 | if err1 != nil { 477 | err = err1 478 | return 479 | } 480 | if n&0x04 > 0 { 481 | break 482 | } 483 | } 484 | lsb, err := r.devRead(commands.CRCResultRegL) 485 | if err != nil { 486 | return 487 | } 488 | res[0] = lsb 489 | 490 | msb, err := r.devRead(commands.CRCResultRegM) 491 | if err != nil { 492 | return 493 | } 494 | res[1] = msb 495 | return 496 | } 497 | 498 | func (r *RFID) SelectTag(serial []byte) (blocks byte, err error) { 499 | dataBuf := make([]byte, len(serial)+2) 500 | dataBuf[0] = commands.PICC_SElECTTAG 501 | dataBuf[1] = 0x70 502 | copy(dataBuf[2:], serial) 503 | crc, err := r.CRC(dataBuf) 504 | if err != nil { 505 | return 506 | } 507 | dataBuf = append(dataBuf, crc[0], crc[1]) 508 | backData, backLen, err := r.cardWrite(commands.PCD_TRANSCEIVE, dataBuf) 509 | if err != nil { 510 | logrus.Warn("Can't select tag ", backData, backLen, err) 511 | return 512 | } 513 | 514 | logrus.Info("Tag info : ", backData, backLen, err) 515 | 516 | if backLen == 0x18 { 517 | blocks = backData[0] 518 | } else { 519 | blocks = 0 520 | } 521 | return 522 | } 523 | 524 | type AuthStatus byte 525 | 526 | const ( 527 | AuthOk AuthStatus = iota 528 | AuthReadFailure 529 | AuthFailure 530 | ) 531 | 532 | func (r *RFID) auth(mode byte, blockAddress byte, sectorKey []byte, serial []byte) (authS AuthStatus, err error) { 533 | buffer := make([]byte, 2) 534 | buffer[0] = mode 535 | buffer[1] = blockAddress 536 | buffer = append(buffer, sectorKey...) 537 | buffer = append(buffer, serial[:4]...) 538 | logrus.Info("CARD Auth: ", printBytes(buffer)) 539 | _, _, err = r.cardWrite(commands.PCD_AUTHENT, buffer) 540 | if err != nil { 541 | logrus.Error(err) 542 | authS = AuthReadFailure 543 | return 544 | } 545 | n, err := r.devRead(commands.Status2Reg) 546 | if err != nil { 547 | logrus.Warn("Can not read device status register") 548 | return 549 | } 550 | if n&0x08 != 0 { 551 | logrus.Debug("N is ", n) 552 | authS = AuthFailure 553 | } 554 | authS = AuthOk 555 | return 556 | } 557 | 558 | func (r *RFID) StopCrypto() (err error) { 559 | err = r.clearBitmask(commands.Status2Reg, 0x08) 560 | return 561 | } 562 | 563 | func (r *RFID) preAccess(blockAddr byte, cmd byte) (data []byte, backLen int, err error) { 564 | send := make([]byte, 4) 565 | send[0] = cmd 566 | send[1] = blockAddr 567 | 568 | crc, err := r.CRC(send[:2]) 569 | if err != nil { 570 | return 571 | } 572 | send[2] = crc[0] 573 | send[3] = crc[1] 574 | logrus.Info("Send access data ", printBytes(send)) 575 | data, backLen, err = r.cardWrite(commands.PCD_TRANSCEIVE, send) 576 | return 577 | } 578 | 579 | func (r *RFID) read(blockAddr byte) (data []byte, err error) { 580 | data, backLen, err := r.preAccess(blockAddr, commands.PICC_READ) 581 | if err != nil { 582 | return 583 | } 584 | logrus.Info("Read data: ", backLen, printBytes(data), err) 585 | if len(data) != 16 { 586 | err = errors.New(fmt.Sprintf("Expected 16 bytes, actual %d", len(data))) 587 | } 588 | return 589 | } 590 | 591 | func (r *RFID) write(blockAddr byte, data []byte) (err error) { 592 | read, backLen, err := r.preAccess(blockAddr, commands.PICC_WRITE) 593 | if err != nil || backLen != 4 { 594 | logrus.Warn("Can not grant Write to block ", read, backLen, err) 595 | return 596 | } 597 | if read[0]&0x0F != 0x0A { 598 | err = errors.New("can't authorize write") 599 | return 600 | } 601 | newData := make([]byte, 18) 602 | copy(newData, data[:16]) 603 | crc, err := r.CRC(newData[:16]) 604 | if err != nil { 605 | logrus.Warn("Can't calculate CRC") 606 | return 607 | } 608 | newData[16] = crc[0] 609 | newData[17] = crc[1] 610 | read, backLen, err = r.cardWrite(commands.PCD_TRANSCEIVE, newData) 611 | if err != nil { 612 | return 613 | } 614 | if backLen != 4 || read[0]&0x0F != 0x0A { 615 | err = errors.New("can not write data") 616 | } 617 | return 618 | } 619 | 620 | func calcBlockAddress(sector int, block int) (addr byte) { 621 | addr = byte(sector*4 + block) 622 | return 623 | } 624 | 625 | func (r *RFID) ReadBlock(sector int, block int) (res []byte, err error) { 626 | res, err = r.read(calcBlockAddress(sector, block%3)) 627 | return 628 | } 629 | 630 | func (r *RFID) WriteBlock(auth byte, sector int, block int, data [16]byte, key []byte) (err error) { 631 | defer func() { 632 | r.StopCrypto() 633 | }() 634 | uuid, err := r.selectCard() 635 | if err != nil { 636 | return 637 | } 638 | state, err := r.Auth(auth, sector, 3, key, uuid) 639 | if err != nil || state != AuthOk { 640 | logrus.Fatal("Can not authenticate ", err, " => ", state) 641 | } 642 | 643 | err = r.write(calcBlockAddress(sector, block%3), data[:]) 644 | return 645 | } 646 | 647 | func (r *RFID) ReadSectorTrail(sector int) (res []byte, err error) { 648 | res, err = r.read(calcBlockAddress(sector&0xFF, 3)) 649 | return 650 | } 651 | 652 | func (r *RFID) WriteSectorTrail(auth byte, sector int, keyA [6]byte, keyB [6]byte, access *BlocksAccess, key []byte) (err error) { 653 | defer func() { 654 | r.StopCrypto() 655 | }() 656 | uuid, err := r.selectCard() 657 | if err != nil { 658 | return 659 | } 660 | state, err := r.Auth(auth, sector, 3, key, uuid) 661 | if err != nil || state != AuthOk { 662 | logrus.Fatal("Can not authenticate ", err, " => ", state) 663 | } 664 | 665 | data := make([]byte, 16) 666 | copy(data, keyA[:]) 667 | accessData := CalculateBlockAccess(access) 668 | copy(data[6:], accessData[:4]) 669 | copy(data[10:], keyB[:]) 670 | err = r.write(calcBlockAddress(sector&0xFF, 3), data) 671 | return 672 | } 673 | 674 | func (r *RFID) Auth(mode byte, sector int, block int, sectorKey []byte, serial []byte) (authS AuthStatus, err error) { 675 | authS, err = r.auth(mode, calcBlockAddress(sector, block), sectorKey, serial) 676 | return 677 | } 678 | 679 | func (r *RFID) selectCard() (uuid []byte, err error) { 680 | err = r.Wait() 681 | if err != nil { 682 | return 683 | } 684 | err = r.Init() 685 | if err != nil { 686 | return 687 | } 688 | _, err = r.Request() 689 | if err != nil { 690 | return 691 | } 692 | uuid, err = r.AntiColl() 693 | if err != nil { 694 | return 695 | } 696 | _, err = r.SelectTag(uuid) 697 | if err != nil { 698 | return 699 | } 700 | return 701 | } 702 | 703 | func (r *RFID) ReadCard(auth byte, sector int, block int, key []byte) (data []byte, err error) { 704 | defer func() { 705 | r.StopCrypto() 706 | }() 707 | uuid, err := r.selectCard() 708 | if err != nil { 709 | return 710 | } 711 | state, err := r.Auth(auth, sector, block, key, uuid) 712 | if err != nil || state != AuthOk { 713 | logrus.Fatal("Can not authenticate ", err, " => ", state) 714 | } 715 | 716 | data, err = r.ReadBlock(sector, block) 717 | 718 | return 719 | } 720 | 721 | func (r *RFID) ReadAuth(auth byte, sector int, key []byte) (data []byte, err error) { 722 | defer func() { 723 | r.StopCrypto() 724 | }() 725 | uuid, err := r.selectCard() 726 | if err != nil { 727 | return 728 | } 729 | state, err := r.Auth(auth, sector, 3, key, uuid) 730 | if err != nil || state != AuthOk { 731 | logrus.Fatal("Can not authenticate ", err, " => ", state) 732 | } 733 | 734 | data, err = r.read(calcBlockAddress(sector, 3)) 735 | return 736 | } 737 | 738 | type BlockAccess byte 739 | 740 | type SectorTrailerAccess byte 741 | 742 | const ( 743 | AnyKeyRWID BlockAccess = iota 744 | RAB_WN_IN_DN = 0x02 // Read (A|B), Write (None), Increment (None), Decrement(None) 745 | RAB_WB_IN_DN = 0x04 746 | RAB_WB_IB_DAB = 0x06 747 | RAB_WN_IN_DAB = 0x01 748 | RB_WB_IN_DN = 0x03 749 | RB_WN_IN_DN = 0x05 750 | RN_WN_IN_DN = 0x07 751 | 752 | KeyA_RN_WA_BITS_RA_WN_KeyB_RA_WA SectorTrailerAccess = iota 753 | KeyA_RN_WN_BITS_RA_WN_KeyB_RA_WN = 0x02 754 | KeyA_RN_WB_BITS_RAB_WN_KeyB_RN_WB = 0x04 755 | KeyA_RN_WN_BITS_RAB_WN_KeyB_RN_WN = 0x06 756 | KeyA_RN_WA_BITS_RA_WA_KeyB_RA_WA = 0x01 757 | KeyA_RN_WB_BITS_RAB_WB_KeyB_RN_WB = 0x03 758 | KeyA_RN_WN_BITS_RAB_WB_KeyB_RN_WN = 0x05 759 | KeyA_RN_WN_BITS_RAB_WN_KeyB_RN_WN_EXTRA = 0x07 760 | ) 761 | 762 | type BlocksAccess struct { 763 | B0, B1, B2 BlockAccess 764 | B3 SectorTrailerAccess 765 | } 766 | 767 | func (ba *BlocksAccess) getBits(bitNum uint) (res byte) { 768 | shift := bitNum - 1 769 | bit := byte(1 << shift) 770 | res = (byte(ba.B0)&bit)>>shift | ((byte(ba.B1)&bit)>>shift)<<1 | ((byte(ba.B2)&bit)>>shift)<<2 | ((byte(ba.B3)&bit)>>shift)<<3 771 | return 772 | } 773 | 774 | func CalculateBlockAccess(ba *BlocksAccess) (res []byte) { 775 | res = make([]byte, 4) 776 | res[0] = (^ba.getBits(1) & 0x0F) | ((^ba.getBits(2) & 0x0F) << 4) 777 | res[1] = (^ba.getBits(3) & 0x0F) | (ba.getBits(1) & 0x0F << 4) 778 | res[2] = (ba.getBits(2) & 0x0F) | (ba.getBits(3) & 0x0F << 4) 779 | res[3] = res[0] ^ res[1] ^ res[2] 780 | return 781 | } 782 | 783 | func ParseBlockAccess(ad []byte) (ba *BlocksAccess) { 784 | ba = new(BlocksAccess) 785 | ba.B0 = BlockAccess(ad[1]&0x10>>4 | ad[2]&0x01<<1 | ad[2]&0x10>>2) 786 | ba.B1 = BlockAccess(ad[1]&0x20>>5 | ad[2]&0x02 | ad[2]&0x20>>3) 787 | ba.B2 = BlockAccess(ad[1]&0x40>>6 | ad[2]&0x04>>1 | ad[2]&0x40>>4) 788 | ba.B3 = SectorTrailerAccess(ad[1]&0x80>>7 | ad[2]&0x08>>2 | ad[2]&0x80>>5) 789 | return 790 | } 791 | -------------------------------------------------------------------------------- /rf522/Rf522_test.go: -------------------------------------------------------------------------------- 1 | package rf522 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stretchr/testify/assert" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | func TestBitCalc(t *testing.T) { 11 | 12 | ba := BlocksAccess{ 13 | B3: KeyA_RN_WB_BITS_RAB_WN_KeyB_RN_WB, // 100 14 | B2: RAB_WN_IN_DAB, // 001 15 | B1: RB_WN_IN_DN, // 101 16 | B0: RAB_WB_IB_DAB, // 110 17 | } 18 | 19 | access := CalculateBlockAccess(&ba) 20 | 21 | reader := func(s string) (res byte) { 22 | d, err := strconv.ParseUint(s, 2, 8) 23 | assert.NoError(t, err) 24 | res = byte(d & 0xFF) 25 | return 26 | } 27 | 28 | assert.Equal(t, reader("0110"), ba.getBits(1)) 29 | 30 | expected := []byte{ 31 | reader("11101001"), 32 | reader("01100100"), 33 | reader("10110001"), 34 | 0, 35 | } 36 | 37 | expected[3] = expected[0] ^ expected[1] ^ expected[2] 38 | 39 | assert.Equal(t, expected, access, "Access is incorrect") 40 | 41 | parsedAccess := ParseBlockAccess(access) 42 | 43 | assert.Equal(t, ba, *parsedAccess, "Parsed access mismatch") 44 | 45 | fmt.Println("Block access", ba, printBytes(access)) 46 | 47 | fmt.Println("Access for FF0780 is", *ParseBlockAccess([]byte{0x80, 0x07, 0xff})) 48 | 49 | } 50 | -------------------------------------------------------------------------------- /rf522/app/rf522_main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/jdevelop/golang-rpi-extras/rf522" 7 | "github.com/jdevelop/golang-rpi-extras/rf522/commands" 8 | "github.com/sirupsen/logrus" 9 | "log" 10 | "os" 11 | "strconv" 12 | ) 13 | 14 | func main() { 15 | 16 | //currentAccessKey := [6]byte{1, 2, 3, 4, 5, 6} 17 | currentAccessKey := [6]byte{6, 5, 4, 3, 2, 1} 18 | currentAccessMethod := byte(commands.PICC_AUTHENT1B) 19 | 20 | sector := flag.Int("s", 1, "card sector") 21 | block := flag.Int("b", 0, "card block") 22 | overwriteKey := flag.Bool("wa", false, "Overwrite keys") 23 | overwriteBlock := flag.Bool("wb", false, "Overwrite block with 0-15") 24 | 25 | flag.Parse() 26 | 27 | // use BCM numbering here 28 | logrus.SetLevel(logrus.DebugLevel) 29 | log.SetOutput(os.Stdout) 30 | rfid, err := rf522.MakeRFID(0, 0, 1000000, 27, 17) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | 35 | data, err := rfid.ReadCard(currentAccessMethod, *sector, *block, currentAccessKey[:]) 36 | auth, err := rfid.ReadAuth(currentAccessMethod, *sector, currentAccessKey[:]) 37 | 38 | access := rf522.ParseBlockAccess(auth[6:10]) 39 | 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | fmt.Printf("RFID sector %d, block %d : %v, auth: %v\n", *sector, *block, data, auth) 45 | fmt.Printf("Permissions: B0: %s, B1: %s, B2: %s, B3/A: %s\n", 46 | strconv.FormatUint(uint64(access.B0), 2), 47 | strconv.FormatUint(uint64(access.B1), 2), 48 | strconv.FormatUint(uint64(access.B2), 2), 49 | strconv.FormatUint(uint64(access.B3), 2), 50 | ) 51 | 52 | if *overwriteBlock { 53 | err = rfid.WriteBlock(currentAccessMethod, 54 | *sector, 55 | *block, 56 | [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 57 | currentAccessKey[:]) 58 | if err != nil { 59 | log.Fatal(err) 60 | } 61 | } 62 | 63 | if *overwriteKey { 64 | err = rfid.WriteSectorTrail(commands.PICC_AUTHENT1A, 65 | *sector, 66 | [6]byte{1, 2, 3, 4, 5, 6}, 67 | [6]byte{6, 5, 4, 3, 2, 1}, 68 | &rf522.BlocksAccess{ 69 | B0: rf522.RAB_WB_IB_DAB, 70 | B1: rf522.RB_WB_IN_DN, 71 | B2: rf522.AnyKeyRWID, 72 | B3: rf522.KeyA_RN_WN_BITS_RAB_WN_KeyB_RN_WN, 73 | }, 74 | currentAccessKey[:], 75 | ) 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | fmt.Println("Write successful") 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /rf522/commands/PICC.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | const ( 4 | PCD_IDLE = 0x00 5 | PCD_AUTHENT = 0x0E 6 | PCD_RECEIVE = 0x08 7 | PCD_TRANSMIT = 0x04 8 | PCD_TRANSCEIVE = 0x0C 9 | PCD_RESETPHASE = 0x0F 10 | PCD_CALCCRC = 0x03 11 | 12 | PICC_REQIDL = 0x26 13 | PICC_REQALL = 0x52 14 | PICC_ANTICOLL = 0x93 15 | PICC_SElECTTAG = 0x93 16 | PICC_AUTHENT1A = 0x60 17 | PICC_AUTHENT1B = 0x61 18 | PICC_READ = 0x30 19 | PICC_WRITE = 0xA0 20 | PICC_DECREMENT = 0xC0 21 | PICC_INCREMENT = 0xC1 22 | PICC_RESTORE = 0xC2 23 | PICC_TRANSFER = 0xB0 24 | PICC_HALT = 0x50 25 | ) 26 | -------------------------------------------------------------------------------- /rf522/commands/Registers.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | const ( 4 | Reserved00 = 0x00 5 | CommandReg = 0x01 6 | CommIEnReg = 0x02 7 | DivlEnReg = 0x03 8 | CommIrqReg = 0x04 9 | DivIrqReg = 0x05 10 | ErrorReg = 0x06 11 | Status1Reg = 0x07 12 | Status2Reg = 0x08 13 | FIFODataReg = 0x09 14 | FIFOLevelReg = 0x0A 15 | WaterLevelReg = 0x0B 16 | ControlReg = 0x0C 17 | BitFramingReg = 0x0D 18 | CollReg = 0x0E 19 | Reserved01 = 0x0F 20 | 21 | Reserved10 = 0x10 22 | ModeReg = 0x11 23 | TxModeReg = 0x12 24 | RxModeReg = 0x13 25 | TxControlReg = 0x14 26 | TxAutoReg = 0x15 27 | TxSelReg = 0x16 28 | RxSelReg = 0x17 29 | RxThresholdReg = 0x18 30 | DemodReg = 0x19 31 | Reserved11 = 0x1A 32 | Reserved12 = 0x1B 33 | MifareReg = 0x1C 34 | Reserved13 = 0x1D 35 | Reserved14 = 0x1E 36 | SerialSpeedReg = 0x1F 37 | 38 | Reserved20 = 0x20 39 | CRCResultRegM = 0x21 40 | CRCResultRegL = 0x22 41 | Reserved21 = 0x23 42 | ModWidthReg = 0x24 43 | Reserved22 = 0x25 44 | RFCfgReg = 0x26 45 | GsNReg = 0x27 46 | CWGsPReg = 0x28 47 | ModGsPReg = 0x29 48 | TModeReg = 0x2A 49 | TPrescalerReg = 0x2B 50 | TReloadRegH = 0x2C 51 | TReloadRegL = 0x2D 52 | TCounterValueRegH = 0x2E 53 | TCounterValueRegL = 0x2F 54 | 55 | Reserved30 = 0x30 56 | TestSel1Reg = 0x31 57 | TestSel2Reg = 0x32 58 | TestPinEnReg = 0x33 59 | TestPinValueReg = 0x34 60 | TestBusReg = 0x35 61 | AutoTestReg = 0x36 62 | VersionReg = 0x37 63 | AnalogTestReg = 0x38 64 | TestDAC1Reg = 0x39 65 | TestDAC2Reg = 0x3A 66 | TestADCReg = 0x3B 67 | Reserved31 = 0x3C 68 | Reserved32 = 0x3D 69 | Reserved33 = 0x3E 70 | Reserved34 = 0x3F 71 | ) 72 | -------------------------------------------------------------------------------- /rf522_main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdevelop/golang-rpi-extras/c75e8edb0d6f44b5ba16f6dcf1f3234f21907dcb/rf522_main -------------------------------------------------------------------------------- /sensor_hcsr04/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ```go 4 | package main 5 | 6 | import ( 7 | hc "github.com/jdevelop/golang-rpi-extras/sensor_hcsr04" 8 | lcd "github.com/jdevelop/golang-rpi-extras/lcd_hd44780" 9 | "fmt" 10 | "time" 11 | ) 12 | 13 | func main() { 14 | 15 | // Use BCM pin numbering 16 | // Echo pin 17 | // Trigger pin 18 | h := hc.NewHCSR04(4, 25) 19 | 20 | myLcd, err := lcd.NewLCD4([]int{27, 22, 23, 24}, 17, 18) 21 | 22 | if err != nil { 23 | panic(err.Error()) 24 | } 25 | 26 | myLcd.Init() 27 | myLcd.Cls() 28 | 29 | for true { 30 | distance := h.MeasureDistance() 31 | fmt.Println(distance) 32 | myLcd.SetCursor(0, 0) 33 | myLcd.Print(fmt.Sprintf("Distance: %5.2f", distance)) 34 | time.Sleep(time.Duration(1) * time.Second) 35 | } 36 | 37 | } 38 | ``` -------------------------------------------------------------------------------- /sensor_hcsr04/sensor_hcsr04.go: -------------------------------------------------------------------------------- 1 | package sensor_hcsr04 2 | 3 | import ( 4 | "github.com/stianeikeland/go-rpio" 5 | "time" 6 | ) 7 | 8 | const HardStop = 1000000 9 | 10 | type HCSR04 struct { 11 | EchoPin rpio.Pin 12 | 13 | PingPin rpio.Pin 14 | } 15 | 16 | func NewHCSR04(echo int, // Echo pin 17 | ping int, // Trigger pin 18 | ) (result HCSR04) { 19 | if err := rpio.Open(); err != nil { 20 | panic(err.Error()) 21 | } 22 | 23 | result.EchoPin = rpio.Pin(echo) 24 | result.PingPin = rpio.Pin(ping) 25 | 26 | return 27 | } 28 | 29 | func (hcsr *HCSR04) MeasureDistance() float32 { 30 | 31 | hcsr.EchoPin.Output() 32 | hcsr.PingPin.Output() 33 | 34 | hcsr.EchoPin.Low() 35 | hcsr.PingPin.Low() 36 | 37 | hcsr.EchoPin.Input() 38 | 39 | strobeZero := 0 40 | strobeOne := 0 41 | 42 | // strobe 43 | delayUs(200) 44 | hcsr.PingPin.High() 45 | delayUs(15) 46 | hcsr.PingPin.Low() 47 | 48 | // wait until strobe back 49 | 50 | for strobeZero = 0; strobeZero < HardStop && hcsr.EchoPin.Read() != rpio.High; strobeZero++ { 51 | } 52 | start := time.Now() 53 | for strobeOne = 0; strobeOne < HardStop && hcsr.EchoPin.Read() != rpio.Low; strobeOne++ { 54 | delayUs(1) 55 | } 56 | end := time.Now() 57 | 58 | return float32(end.UnixNano()-start.UnixNano()) / (58.0 * 1000) 59 | } 60 | 61 | func delayUs(ms int) { 62 | time.Sleep(time.Duration(ms) * time.Microsecond) 63 | } 64 | -------------------------------------------------------------------------------- /ssd1306/Constants.go: -------------------------------------------------------------------------------- 1 | package ssd1306 2 | 3 | const ( 4 | SSD1306_DISPLAYON = 0xAF 5 | SSD1306_I2C_ADDRESS = 0x3C // 011110+SA0+RW - 0x3C or 0x3D 6 | SSD1306_SETCONTRAST = 0x81 7 | SSD1306_DISPLAYALLON_RESUME = 0xA4 8 | SSD1306_DISPLAYALLON = 0xA5 9 | SSD1306_NORMALDISPLAY = 0xA6 10 | SSD1306_INVERTDISPLAY = 0xA7 11 | SSD1306_DISPLAYOFF = 0xAE 12 | SSD1306_SETDISPLAYOFFSET = 0xD3 13 | SSD1306_SETCOMPINS = 0xDA 14 | SSD1306_SETVCOMDETECT = 0xDB 15 | SSD1306_SETDISPLAYCLOCKDIV = 0xD5 16 | SSD1306_SETPRECHARGE = 0xD9 17 | SSD1306_SETMULTIPLEX = 0xA8 18 | SSD1306_SETLOWCOLUMN = 0x00 19 | SSD1306_SETHIGHCOLUMN = 0x10 20 | SSD1306_SETSTARTLINE = 0x40 21 | SSD1306_MEMORYMODE = 0x20 22 | SSD1306_COLUMNADDR = 0x21 23 | SSD1306_PAGEADDR = 0x22 24 | SSD1306_COMSCANINC = 0xC0 25 | SSD1306_COMSCANDEC = 0xC8 26 | SSD1306_SEGREMAP = 0xA0 27 | SSD1306_CHARGEPUMP = 0x8D 28 | SSD1306_EXTERNALVCC = 0x1 29 | SSD1306_SWITCHCAPVCC = 0x2 30 | 31 | // scrolling 32 | 33 | SSD1306_ACTIVATE_SCROLL = 0x2F 34 | SSD1306_DEACTIVATE_SCROLL = 0x2E 35 | SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3 36 | SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26 37 | SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27 38 | SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29 39 | SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A 40 | ) 41 | -------------------------------------------------------------------------------- /ssd1306/OLEDInterface.go: -------------------------------------------------------------------------------- 1 | package ssd1306 2 | -------------------------------------------------------------------------------- /ssd1306/oled_i2c/OLEDControl.go: -------------------------------------------------------------------------------- 1 | package oled_i2c 2 | 3 | // Modified from the original code at https://github.com/goiot/devices/tree/master/monochromeoled 4 | // Works with 128x62 and 128x32 5 | 6 | import ( 7 | "fmt" 8 | "image" 9 | 10 | "github.com/jdevelop/golang-rpi-extras/ssd1306" 11 | "golang.org/x/exp/io/i2c" 12 | "golang.org/x/exp/io/i2c/driver" 13 | ) 14 | 15 | // OLED represents an SSD1306 OLED display. 16 | type OLED struct { 17 | dev *i2c.Device 18 | 19 | w int // width of the display 20 | h int // height of the display 21 | buf []byte // each pixel is represented by a bit 22 | } 23 | 24 | // Open opens an SSD1306 OLED display. Once not in use, it needs to 25 | // be close by calling Close. 26 | // The default width is 128, height is 64 if zero values are given. 27 | func Open(o driver.Opener, addr, w, h int) (*OLED, error) { 28 | dev, err := i2c.Open(o, addr) 29 | buf := make([]byte, w*(h/8)+1) 30 | buf[0] = ssd1306.SSD1306_SETSTARTLINE // start frame of pixel data 31 | oled := &OLED{dev: dev, w: w, h: h, buf: buf} 32 | err = oled.Init() 33 | if err != nil { 34 | return nil, err 35 | } 36 | return oled, nil 37 | } 38 | 39 | var initSeq = []byte{ 40 | 0xAE, 0xA8, 0x3F, 0xD3, 0x00, 0x40, 0xA1, 0xC8, 41 | 0xA6, 0xD5, 0x80, 0xDA, 0x12, 0x81, 0x00, 0xB0, 42 | 0xA4, 0xDB, 0x40, 0x20, 0x00, 0x00, 0x10, 0x8D, 43 | 0x14, 0x2E, 0xA6, 0xAF, 44 | } 45 | 46 | // Init sets up the display for writing 47 | func (o *OLED) Init() (err error) { 48 | err = o.writeI2CCmd(initSeq) 49 | return 50 | } 51 | 52 | // On turns on the display if it is off. 53 | func (o *OLED) On() error { 54 | return o.dev.Write([]byte{0x0, ssd1306.SSD1306_DISPLAYON}) 55 | } 56 | 57 | // Off turns off the display if it is on. 58 | func (o *OLED) Off() error { 59 | return o.dev.Write([]byte{0x0, ssd1306.SSD1306_DISPLAYOFF}) 60 | } 61 | 62 | // Clear clears the entire display. 63 | func (o *OLED) Clear() error { 64 | for i := 1; i < len(o.buf); i++ { 65 | o.buf[i] = 0 66 | } 67 | return o.Draw() 68 | } 69 | 70 | // SetPixel set and x,y pixel to on or off 71 | func (o *OLED) SetPixel(x, y int, v byte) error { 72 | if x >= o.w || y >= o.h { 73 | return fmt.Errorf("(x=%v, y=%v) is out of bounds on this %vx%v display", x, y, o.w, o.h) 74 | } 75 | if v > 1 { 76 | return fmt.Errorf("value needs to be either 0 or 1; given %v", v) 77 | } 78 | i := 1 + x + (y/8)*o.w 79 | if v == 0 { 80 | o.buf[i] &= ^(1 << uint((y & 7))) 81 | } else { 82 | o.buf[i] |= 1 << uint((y & 7)) 83 | } 84 | return nil 85 | } 86 | 87 | // SetImage draws an image on the display buffer starting from x, y. 88 | // A call to Draw is required to display it on the OLED display. 89 | func (o *OLED) SetImage(x, y int, img image.Image) error { 90 | imgW := img.Bounds().Dx() 91 | imgH := img.Bounds().Dy() 92 | 93 | endX := x + imgW 94 | endY := y + imgH 95 | 96 | if endX >= o.w { 97 | endX = o.w 98 | } 99 | if endY >= o.h { 100 | endY = o.h 101 | } 102 | 103 | var imgI, imgY int 104 | for i := x; i < endX; i++ { 105 | imgY = 0 106 | for j := y; j < endY; j++ { 107 | r, g, b, _ := img.At(imgI, imgY).RGBA() 108 | var v byte 109 | if r+g+b > 0 { 110 | v = 0x1 111 | } 112 | if err := o.SetPixel(i, j, v); err != nil { 113 | return err 114 | } 115 | imgY++ 116 | } 117 | imgI++ 118 | } 119 | return nil 120 | } 121 | 122 | // Draw draws the intermediate pixel buffer on the display. 123 | // See SetPixel and SetImage to mutate the buffer. 124 | func (o *OLED) Draw() error { 125 | if err := o.writeI2CCmd([]byte{ 126 | ssd1306.SSD1306_DISPLAYALLON_RESUME, // write mode 127 | ssd1306.SSD1306_SETSTARTLINE | 0, // start line = 0 128 | ssd1306.SSD1306_COLUMNADDR, 0, uint8(o.w), 129 | ssd1306.SSD1306_PAGEADDR, 0, 7, 130 | }); err != nil { // the write mode 131 | return err 132 | } 133 | return o.dev.Write(o.buf) 134 | } 135 | 136 | // EnableScroll starts scrolling in the horizontal direction starting from 137 | // startY column to endY column. 138 | func (o *OLED) EnableScroll(startY, endY int) error { 139 | panic("not implemented") 140 | } 141 | 142 | // DisableScroll stops the scrolling on the display. 143 | func (o *OLED) DisableScroll() error { 144 | panic("not implemented") 145 | } 146 | 147 | // Width returns the display width. 148 | func (o *OLED) Width() int { return o.w } 149 | 150 | // Height returns the display height. 151 | func (o *OLED) Height() int { return o.h } 152 | 153 | // Close closes the display. 154 | func (o *OLED) Close() error { 155 | return o.dev.Close() 156 | } 157 | 158 | func (o *OLED) writeI2CCmd(commands []byte) (err error) { 159 | data := make([]byte, 2) 160 | for _, v := range initSeq { 161 | data[1] = v 162 | err = o.dev.Write(data) 163 | if err != nil { 164 | return 165 | } 166 | } 167 | return 168 | } 169 | -------------------------------------------------------------------------------- /ssd1306/oled_spi/OLEDControl.go: -------------------------------------------------------------------------------- 1 | package oled_spi 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/ecc1/spi" 7 | ssd "github.com/jdevelop/golang-rpi-extras/ssd1306" 8 | "github.com/jdevelop/gpio" 9 | "image" 10 | "image/color" 11 | "time" 12 | ) 13 | 14 | type SSD1306 struct { 15 | spiDev *spi.Device 16 | Width, Height, Pages uint 17 | reset, dc gpio.Pin 18 | buffer []byte 19 | cmd []byte 20 | } 21 | 22 | type SSD1306Setup struct { 23 | BusId, DevId, ResetPin, DcPin uint 24 | Width, Height uint 25 | } 26 | 27 | func defaultSetup() *SSD1306Setup { 28 | return &SSD1306Setup{ 29 | BusId: 0, 30 | DevId: 0, 31 | DcPin: 17, 32 | ResetPin: 27, 33 | Width: 128, 34 | Height: 96, 35 | } 36 | } 37 | 38 | type Setup func(*SSD1306Setup) 39 | 40 | func Width(w uint) Setup { 41 | return func(c *SSD1306Setup) { 42 | c.Width = w 43 | } 44 | } 45 | 46 | func Height(w uint) Setup { 47 | return func(c *SSD1306Setup) { 48 | c.Height = w 49 | } 50 | } 51 | 52 | func MakeSSD1306(funcs ...Setup) (display *SSD1306, err error) { 53 | ssd := defaultSetup() 54 | for _, s := range funcs { 55 | s(ssd) 56 | } 57 | spiDev, err := spi.Open(fmt.Sprintf("/dev/spidev%d.%d", ssd.BusId, ssd.DevId), 8000000, 0) 58 | if err != nil { 59 | return 60 | } 61 | spiDev.SetLSBFirst(false) 62 | spiDev.SetBitsPerWord(8) 63 | 64 | dsp := SSD1306{ 65 | Height: ssd.Height, 66 | Width: ssd.Width, 67 | } 68 | 69 | display = &dsp 70 | 71 | dsp.Pages = dsp.Height / 8 72 | 73 | dsp.buffer = make([]byte, dsp.Width*dsp.Pages) 74 | 75 | dsp.spiDev = spiDev 76 | 77 | dsp.cmd = make([]byte, 1) 78 | 79 | dsp.reset, err = gpio.OpenPin(int(ssd.ResetPin), gpio.ModeOutput) 80 | if err != nil { 81 | return 82 | } 83 | 84 | dsp.dc, err = gpio.OpenPin(int(ssd.DcPin), gpio.ModeOutput) 85 | if err != nil { 86 | return 87 | } 88 | 89 | return 90 | } 91 | 92 | func (s *SSD1306) Command(cmd byte) (err error) { 93 | s.dc.Clear() 94 | s.cmd[0] = cmd 95 | err = s.spiDev.Write(s.cmd) 96 | return 97 | } 98 | 99 | func (s *SSD1306) Commands(cmd []byte) (err error) { 100 | s.dc.Clear() 101 | err = s.spiDev.Write(cmd) 102 | return 103 | } 104 | 105 | func (s *SSD1306) Data(data []byte) (err error) { 106 | s.dc.Set() 107 | err = s.spiDev.Write(data) 108 | return 109 | } 110 | 111 | func (s *SSD1306) Reset() (err error) { 112 | s.reset.Set() 113 | time.Sleep(time.Millisecond) 114 | s.reset.Clear() 115 | time.Sleep(10 * time.Millisecond) 116 | s.reset.Set() 117 | return 118 | } 119 | 120 | func (s *SSD1306) Start() (err error) { 121 | err = s.Reset() 122 | if err != nil { 123 | return 124 | } 125 | s.Init() 126 | err = s.Command(ssd.SSD1306_DISPLAYON) 127 | return 128 | } 129 | 130 | var initCmds = []byte{ssd.SSD1306_DISPLAYOFF, ssd.SSD1306_SETDISPLAYCLOCKDIV, 0x80, 131 | ssd.SSD1306_SETMULTIPLEX, 0x3F, ssd.SSD1306_SETDISPLAYOFFSET, 132 | 0x0, ssd.SSD1306_SETSTARTLINE | 0x0, ssd.SSD1306_CHARGEPUMP, 0x10, 133 | ssd.SSD1306_MEMORYMODE, 0x00, ssd.SSD1306_SEGREMAP | 0x1, ssd.SSD1306_COMSCANDEC, ssd.SSD1306_SETCOMPINS, 134 | 0x12, ssd.SSD1306_SETCONTRAST, 0x9F, ssd.SSD1306_SETPRECHARGE, 0x22, 135 | ssd.SSD1306_SETVCOMDETECT, 0x40, ssd.SSD1306_DISPLAYALLON_RESUME, 136 | ssd.SSD1306_NORMALDISPLAY} 137 | 138 | func (s *SSD1306) Init() (err error) { 139 | s.Commands(initCmds) 140 | return 141 | } 142 | 143 | var refreshCmdBuf = []byte{ssd.SSD1306_COLUMNADDR, 0, 0, ssd.SSD1306_PAGEADDR, 0, 0} 144 | 145 | func (s *SSD1306) Refresh() (err error) { 146 | refreshCmdBuf[2] = byte(s.Width - 1) 147 | refreshCmdBuf[5] = byte(s.Pages - 1) 148 | for _, cmdByte := range refreshCmdBuf { 149 | err = s.Command(cmdByte) 150 | if err != nil { 151 | return 152 | } 153 | } 154 | s.dc.Set() 155 | err = s.spiDev.Write(s.buffer) 156 | return 157 | } 158 | 159 | func (s *SSD1306) Clear() { 160 | for i := range s.buffer { 161 | s.buffer[i] = 0 162 | } 163 | } 164 | 165 | func (s *SSD1306) Image(img image.Image) (err error) { 166 | dim := img.Bounds() 167 | if uint(dim.Max.X) > s.Width || uint(dim.Max.Y) > s.Height { 168 | err = errors.New("Image dimensions are not correct") 169 | return 170 | } 171 | byteIdx := 0 172 | for page := uint(0); page < s.Pages; page++ { 173 | for column := uint(0); column < s.Width; column++ { 174 | bits := byte(0) 175 | for bit := uint(0); bit < 8; bit++ { 176 | if c := img.At(int(column), int(page*8+7-bit)); c != color.Black { 177 | bits = bits | (1 << bit) 178 | } 179 | } 180 | s.buffer[byteIdx] = bits 181 | byteIdx = byteIdx + 1 182 | } 183 | } 184 | return 185 | } 186 | 187 | func (s *SSD1306) Contrast(contrast byte) (err error) { 188 | err = s.Command(ssd.SSD1306_SETCONTRAST) 189 | if err != nil { 190 | return 191 | } 192 | err = s.Command(contrast) 193 | return 194 | } 195 | --------------------------------------------------------------------------------