├── LICENSE ├── LaserShow ├── Basics.cpp ├── Basics.h ├── Cube.cpp ├── Cube.h ├── DAC_MCP4X.cpp ├── DAC_MCP4X.h ├── Drawing.cpp ├── Drawing.h ├── Font.h ├── Laser.cpp ├── Laser.h ├── LaserShow.ino ├── Logo.h └── Objects.h ├── LaserSpectrumAnalyzer ├── Basics.cpp ├── Basics.h ├── DAC_MCP4X.cpp ├── DAC_MCP4X.h ├── Drawing.cpp ├── Drawing.h ├── Font.h ├── LICENSE.txt ├── Laser.cpp ├── Laser.h └── LaserSpectrumAnalyzer.ino ├── README.md └── Scripts └── convertGCode.py /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 | -------------------------------------------------------------------------------- /LaserShow/Basics.cpp: -------------------------------------------------------------------------------- 1 | #include "Basics.h" 2 | #include "Arduino.h" 3 | 4 | // Copied from "Arduino - Tiny 3D Engine" by Themistokle "mrt-prodz" Benetatos. 5 | // https://github.com/mrt-prodz/ATmega328-Tiny-3D-Engine 6 | // (and adapted for laser/quad rendering) 7 | 8 | #define LUT(a) (long)(pgm_read_word(&lut[a]))// return value from LUT in PROGMEM 9 | 10 | const unsigned int lut[] PROGMEM = { // 0 to 90 degrees fixed point COSINE look up table 11 | 16384, 16381, 16374, 16361, 16344, 16321, 16294, 16261, 16224, 16182, 16135, 16082, 16025, 15964, 15897, 15825, 15749, 15668, 15582, 15491, 15395, 15295, 15190, 15081, 14967, 14848, 14725, 14598, 14466, 14329, 14188, 14043, 13894, 13740, 13582, 13420, 13254, 13084, 12910, 12732, 12550, 12365, 12175, 11982, 11785, 11585, 11381, 11173, 10963, 10748, 10531, 10310, 10086, 9860, 9630, 9397, 9161, 8923, 8682, 8438, 8191, 7943, 7691, 7438, 7182, 6924, 6663, 6401, 6137, 5871, 5603, 5334, 5062, 4790, 4516, 4240, 3963, 3685, 3406, 3126, 2845, 2563, 2280, 1996, 1712, 1427, 1142, 857, 571, 285, 0 12 | }; 13 | 14 | // ---------------------------------------------- 15 | // SIN/COS from 90 degrees LUT 16 | // ---------------------------------------------- 17 | long SIN(unsigned int angle) { 18 | angle += 90; 19 | if (angle > 450) return LUT(0); 20 | if (angle > 360 && angle < 451) return -LUT(angle-360); 21 | if (angle > 270 && angle < 361) return -LUT(360-angle); 22 | if (angle > 180 && angle < 271) return LUT(angle-180); 23 | return LUT(180-angle); 24 | } 25 | 26 | long COS(unsigned int angle) { 27 | if (angle > 360) return LUT(0); 28 | if (angle > 270 && angle < 361) return LUT(360-angle); 29 | if (angle > 180 && angle < 271) return -LUT(angle-180); 30 | if (angle > 90 && angle < 181) return -LUT(180-angle); 31 | return LUT(angle); 32 | } 33 | 34 | // fixed point multiplication 35 | static long pMultiply(long x, long y) { 36 | return ( (x * y) + PROUNDBIT) >> PSHIFT; 37 | } 38 | 39 | // ---------------------------------------------- 40 | // Matrix operation 41 | // ---------------------------------------------- 42 | void Matrix3::multiply(const Matrix3 &mat1, const Matrix3 &mat2, Matrix3& mat ) { 43 | unsigned char r,c; 44 | for (c=0; c<3; c++) 45 | for (r=0; r<3; r++) 46 | mat.m[c][r] = pMultiply(mat1.m[0][r], mat2.m[c][0]) + 47 | pMultiply(mat1.m[1][r], mat2.m[c][1]) + 48 | pMultiply(mat1.m[2][r], mat2.m[c][2]); 49 | } 50 | 51 | Matrix3 Matrix3::rotateX(const unsigned int angle) { 52 | Matrix3 mat; 53 | mat.m[1][1] = COS(angle); 54 | mat.m[1][2] = SIN(angle); 55 | mat.m[2][1] = -SIN(angle); 56 | mat.m[2][2] = COS(angle); 57 | return mat; 58 | } 59 | 60 | Matrix3 Matrix3::rotateY(const unsigned int angle) { 61 | Matrix3 mat; 62 | mat.m[0][0] = COS(angle); 63 | mat.m[0][2] = -SIN(angle); 64 | mat.m[2][0] = SIN(angle); 65 | mat.m[2][2] = COS(angle); 66 | return mat; 67 | } 68 | 69 | Matrix3 Matrix3::rotateZ(const unsigned int angle) { 70 | Matrix3 mat; 71 | mat.m[0][0] = COS(angle); 72 | mat.m[0][1] = SIN(angle); 73 | mat.m[1][0] = -SIN(angle); 74 | mat.m[1][1] = COS(angle); 75 | return mat; 76 | } 77 | 78 | void Matrix3::applyMatrix(const Matrix3& matrix, const Vector3i& in, Vector3i& out) 79 | { 80 | out.x = (matrix.m[0][0] * in.x + 81 | matrix.m[1][0] * in.y + 82 | matrix.m[2][0] * in.z) >> PSHIFT; 83 | 84 | out.y = (matrix.m[0][1] * in.x + 85 | matrix.m[1][1] * in.y + 86 | matrix.m[2][1] * in.z) >> PSHIFT; 87 | 88 | out.z = (matrix.m[0][2] * in.x + 89 | matrix.m[1][2] * in.y + 90 | matrix.m[2][2] * in.z) >> PSHIFT; 91 | 92 | } 93 | 94 | -------------------------------------------------------------------------------- /LaserShow/Basics.h: -------------------------------------------------------------------------------- 1 | #ifndef BASICS_H 2 | #define BASICS_H 3 | 4 | // typedef for fix point numbers 5 | typedef long FIXPT; 6 | #define PRES 16384 7 | #define PSHIFT 14 8 | #define PROUNDBIT (1 << (PSHIFT-1)) 9 | #define FROM_FLOAT(a) (long(a*PRES)) 10 | #define FROM_INT(a) (a << PSHIFT) 11 | #define TO_INT(a) ((a + PROUNDBIT)>> PSHIFT) 12 | 13 | typedef struct { 14 | int x, y, z; 15 | } Vector3i; 16 | 17 | // fixed point identity matrix 18 | struct Matrix3 { 19 | long m[3][3] = { 20 | {PRES, 0, 0}, 21 | { 0, PRES, 0}, 22 | { 0, 0, PRES} 23 | }; 24 | static void applyMatrix(const Matrix3& matrix, const Vector3i& in, Vector3i& out); 25 | // due to a GCC compiler bug, I could not return a Matrix3. Make sure that 26 | // mat1 and mat2 are != result! 27 | static void multiply(const Matrix3 &mat1, const Matrix3 &mat2, Matrix3& result); 28 | static Matrix3 rotateX(const unsigned int angle); 29 | static Matrix3 rotateY(const unsigned int angle); 30 | static Matrix3 rotateZ(const unsigned int angle); 31 | }; 32 | 33 | 34 | long SIN(unsigned int angle); 35 | long COS(unsigned int angle); 36 | 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /LaserShow/Cube.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Cube.h" 3 | 4 | // Copied from "Arduino - Tiny 3D Engine" by Themistokle "mrt-prodz" Benetatos. 5 | // https://github.com/mrt-prodz/ATmega328-Tiny-3D-Engine 6 | // (and adapted for laser/quad rendering) 7 | 8 | // ---------------------------------------------- 9 | // functions 10 | // ---------------------------------------------- 11 | 12 | #define NODECOUNT 8 13 | #define TRICOUNT 6 14 | 15 | #define NODE(a, b) (long)(pgm_read_dword(&nodes[a][b])) 16 | #define EDGE(a, b) pgm_read_byte(&faces[a][b]) 17 | #define EDGECODE(a, b) pgm_read_byte(&edgecodes[a][b]) 18 | 19 | const long nodes[NODECOUNT][3] PROGMEM = { 20 | { 1500, 1500, -1500}, 21 | { 1500, -1500, -1500}, 22 | {-1500, -1500, -1500}, 23 | {-1500, 1500, -1500}, 24 | { 1500, 1500, 1500}, 25 | {-1500, 1500, 1500}, 26 | {-1500, -1500, 1500}, 27 | { 1500, -1500, 1500}, 28 | }; 29 | 30 | const unsigned char faces[TRICOUNT][4] PROGMEM = { 31 | {0, 1, 2,3}, 32 | {4, 5, 6,7}, 33 | {0, 4, 7,1}, 34 | {1, 7, 6,2}, 35 | {2, 6, 5,3}, 36 | {4, 0, 3, 5}, 37 | }; 38 | 39 | const unsigned char edgecodes[TRICOUNT][4] PROGMEM = { 40 | {0, 1, 2,3}, 41 | {7, 6, 5,4}, 42 | {8,4,9,0}, 43 | {9, 5, 10,1}, 44 | {10, 6, 11,2}, 45 | {8, 7, 11,3}, 46 | }; 47 | 48 | // ---------------------------------------------- 49 | // global variables 50 | // ---------------------------------------------- 51 | Matrix3 m_world; 52 | Vector3i mesh_rotation = {0, 0, 0}; 53 | Vector3i mesh_position = {0, 0, 0}; 54 | 55 | 56 | static long proj_nodes[NODECOUNT][2]; // projected nodes (x,y) 57 | static unsigned char i; 58 | 59 | 60 | // ---------------------------------------------- 61 | // Shoelace algorithm to get the surface 62 | // ---------------------------------------------- 63 | int shoelace(const int (*n)[2], const unsigned char index) { 64 | unsigned char t = 0; 65 | int surface = 0; 66 | for (; t<3; t++) { 67 | // (x1y2 - y1x2) + (x2y3 - y2x3) ... 68 | surface += (n[EDGE(index,t)][0] * n[EDGE(index,(t<2?t+1:0))][1]) - 69 | (n[EDGE(index,(t<2?t+1:0))][0] * n[EDGE(index,t)][1]); 70 | } 71 | return surface * 0.5; 72 | } 73 | 74 | // ---------------------------------------------- 75 | // Shoelace algorithm for triangle visibility 76 | // ---------------------------------------------- 77 | bool is_hidden(const long (*n)[2], const unsigned char index) { 78 | // (x1y2 - y1x2) + (x2y3 - y2x3) ... 79 | return ( ( (n[EDGE(index,0)][0] * n[EDGE(index,1)][1]) - 80 | (n[EDGE(index,1)][0] * n[EDGE(index,0)][1]) ) + 81 | ( (n[EDGE(index,1)][0] * n[EDGE(index,2)][1]) - 82 | (n[EDGE(index,2)][0] * n[EDGE(index,1)][1]) ) + 83 | ( (n[EDGE(index,2)][0] * n[EDGE(index,0)][1]) - 84 | (n[EDGE(index,0)][0] * n[EDGE(index,2)][1]) ) ) < 0 ? false : true; 85 | } 86 | 87 | void draw_wireframe_quads(const long (*n)[2]) { 88 | i = TRICOUNT-1; 89 | int edges[12]; 90 | for (int i = 0; i<12;i++) edges[i]=0; 91 | do { 92 | // don't draw triangle with negative surface value 93 | if (!is_hidden(n, i)) { 94 | // draw triangle edges - 0 -> 1 -> 2 -> 0 95 | int code = EDGECODE(i,0); 96 | if (edges[code] == 0) 97 | { 98 | edges[code] = 1; 99 | laser.drawline(n[EDGE(i,0)][0], n[EDGE(i,0)][1], n[EDGE(i,1)][0], n[EDGE(i,1)][1]); 100 | } 101 | code = EDGECODE(i,1); 102 | if (edges[code] == 0){ 103 | edges[code] = 1; 104 | laser.drawline(n[EDGE(i,1)][0], n[EDGE(i,1)][1], n[EDGE(i,2)][0], n[EDGE(i,2)][1]); 105 | } 106 | code = EDGECODE(i,2); 107 | if (edges[code] == 0){ 108 | edges[code] = 1; 109 | laser.drawline(n[EDGE(i,2)][0], n[EDGE(i,2)][1], n[EDGE(i,3)][0], n[EDGE(i,3)][1]); 110 | } 111 | code = EDGECODE(i,3); 112 | if (edges[code] == 0){ 113 | edges[code] = 1; 114 | laser.drawline(n[EDGE(i,3)][0], n[EDGE(i,3)][1], n[EDGE(i,0)][0], n[EDGE(i,0)][1]); 115 | } 116 | } 117 | } while(i--); 118 | } 119 | 120 | void rotateCube(int count) { 121 | laser.setScale(1); 122 | laser.setOffset(2048,2048); 123 | float scale = 1.; 124 | for (int lo = 0;lo 360) mesh_rotation.x = 0; 153 | if (mesh_rotation.y > 360) mesh_rotation.y = 0; 154 | if (mesh_rotation.z > 360) mesh_rotation.z = 0; 155 | draw_wireframe_quads(proj_nodes); 156 | laser.off(); 157 | // keep rotation locked to maximum time the cube takes, which is 20000 micros 158 | long elapsed = micros() - time; 159 | if (elapsed < 20000) { delayMicroseconds(20000-elapsed); } 160 | // do an intro/extro animation 161 | if (lo < 120) { 162 | laser.setOffset(2048 - (120-lo)*20,2048); 163 | } 164 | if (lo > count - 60) { 165 | laser.setScale(scale); 166 | scale -= 0.02; 167 | if (scale <0) { scale = 0; } 168 | } 169 | } 170 | } 171 | 172 | -------------------------------------------------------------------------------- /LaserShow/Cube.h: -------------------------------------------------------------------------------- 1 | #ifndef CUBE_H 2 | #define CUBE_H 3 | 4 | #include "Laser.h" 5 | 6 | extern Laser laser; 7 | 8 | void rotateCube(int count); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /LaserShow/DAC_MCP4X.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Microchip MCP4901 / MCP4911 / MCP4921 / MCP4902 / MCP4912 / MCP4922 8/10/12-bit DAC driver 3 | * 4 | * See header file. 5 | */ 6 | 7 | #include 8 | #include "DAC_MCP4X.h" 9 | 10 | 11 | byte MCP4X::init(byte model, unsigned int vrefA, unsigned int vrefB, 12 | int ss_pin, int ldac_pin, boolean autoLatch) { 13 | 14 | const byte resolutions[] = {8,10,12}; 15 | 16 | bitwidth = resolutions[model & 2]; 17 | dual = model & MCP4X_DUAL; 18 | 19 | if (model & MCP4X_INTREF) 20 | vrefA = vrefB = 2048; 21 | this->vrefs[0] = vrefA; 22 | this->vrefs[1] = vrefB; 23 | 24 | this->ss_pin = ss_pin; 25 | this->LDAC_pin = ldac_pin; 26 | this->autoLatch = autoLatch; 27 | 28 | regs[0] = MCP4X_DEFAULTS; 29 | regs[1] = MCP4X_WRITE_B | MCP4X_DEFAULTS; 30 | 31 | return 1; 32 | } 33 | 34 | void MCP4X::begin(boolean beginSPI) { 35 | 36 | pinMode(ss_pin, OUTPUT); // Ensure that SS is set to SPI master mode 37 | pinMode(LDAC_pin, OUTPUT); 38 | 39 | digitalWrite(ss_pin, HIGH); // Unselect the device 40 | digitalWrite(LDAC_pin, HIGH); // Un-latch the output 41 | 42 | if (beginSPI) 43 | SPI.begin(); 44 | configureSPI(); 45 | 46 | 47 | } 48 | 49 | void MCP4X::configureSPI() { 50 | SPI.setBitOrder(MSBFIRST); 51 | SPI.setDataMode(SPI_MODE0); 52 | SPI.setClockDivider(SPI_CLOCK_DIV2); 53 | } 54 | 55 | // Sets the gain. These DACs support 1x and 2x gain. 56 | // vout = x/2^n * gain * VREF, where x = the argument to out(), n = number of DAC bits 57 | // Example: with 1x gain, set(100) on a 8-bit (256-step) DAC would give 58 | // an output voltage of 100/256 * VREF, while a gain of 2x would give 59 | // vout = 100/256 * VREF * 2 60 | void MCP4X::setGain2x(byte chan, boolean gain2x) { 61 | chan &= 0x1; 62 | if (gain2x) 63 | regs[chan] &= ~MCP4X_GAIN_1X; 64 | else 65 | regs[chan] |= MCP4X_GAIN_1X; 66 | } 67 | 68 | // Shuts the DAC down. Shutdown current is about 1/50 (typical) of active mode current. 69 | // My measurements say ~160-180 µA active (unloaded vout), ~3.5 µA shutdown. 70 | // Time to settle on an output value increases from ~4.5 µs to ~10 µs, though (according to the datasheet). 71 | void MCP4X::shutdown(byte chan, boolean off) { 72 | 73 | chan &= 0x1; 74 | 75 | if (off) 76 | regs[chan] &= ~MCP4X_ACTIVE; 77 | else 78 | regs[chan] |= MCP4X_ACTIVE; 79 | 80 | write(regs[chan]); 81 | } 82 | 83 | void MCP4X::setVoltage(byte chan, float v) { 84 | float data; 85 | 86 | chan &= 0x1; 87 | 88 | data = (v * 1000 * (1 << bitwidth)) / ((unsigned long) vrefs[chan] * getGain(chan)); 89 | 90 | // Serial.print("bitwidth = "); 91 | // Serial.println(bitwidth); 92 | // Serial.print("Vref = "); 93 | // Serial.println(vrefs[chan]); 94 | // Serial.print("gain = "); 95 | // Serial.println(getGain(chan)); 96 | // Serial.print("V = "); 97 | // Serial.print(v); 98 | // Serial.print(" V ["); 99 | // Serial.print(data); 100 | // Serial.print(" ["); 101 | // Serial.print((int)round(data)); 102 | // Serial.println("]"); 103 | 104 | output(chan, (int)round(data)); 105 | 106 | // Serial.print("=> V = "); 107 | // Serial.print(getVoltageMV(chan)); 108 | // Serial.println("mV"); 109 | } 110 | 111 | float MCP4X::getVoltageMV(byte chan) { 112 | unsigned int data; 113 | 114 | chan &= 1; 115 | 116 | data = (regs[chan] >> (12 - bitwidth)) & ((1 << bitwidth) - 1); 117 | 118 | Serial.println(data, HEX); 119 | 120 | return ((float) vrefs[chan] * data) / (1 << bitwidth) * getGain(chan); 121 | } 122 | 123 | // Called by the output* set of functions. 124 | void MCP4X::output2(unsigned short data_A, unsigned short data_B) { 125 | this->output(MCP4X_CHAN_A, data_A); 126 | this->output(MCP4X_CHAN_B, data_B); 127 | 128 | // Update the output, if desired. 129 | // The reason this is only in the dual-output version is simple: it's mostly useless 130 | // for the single-output version, as it would make more sense to tie the \LDAC pin 131 | // to ground, or do it manually. However, there should be a single-call method 132 | // to update *both* channels in sync, which wouldn't be possible with multiple 133 | // separate DACs (for which there is latch()). 134 | if (autoLatch) { 135 | this->latch(); 136 | } 137 | } 138 | 139 | void MCP4X::output(byte chan, unsigned short data) { 140 | // unsigned int out; 141 | 142 | const unsigned short maxval = (1 << bitwidth) - 1; 143 | 144 | chan &= 0x1; 145 | 146 | 147 | if (data > maxval) 148 | data = maxval; 149 | // data = constrain(data, 0, (1 << bitwidth) - 1); 150 | 151 | // Truncate the unused bits to fit the 8/10/12 bits the DAC accepts 152 | // if (this->bitwidth == 12) 153 | // data &= 0xfff; 154 | // else if (this->bitwidth == 10) 155 | // data &= 0x3ff; 156 | // else if (this->bitwidth == 8) 157 | // data &= 0xff; 158 | // data &= (1 << bitwidth) - 1; 159 | 160 | // clear value bits 161 | regs[chan] &= 0xF000; 162 | 163 | regs[chan] |= data << (12 - bitwidth); 164 | 165 | write(regs[chan]); 166 | // // bit 15: 0 for DAC A, 1 for DAC B. (Always 0 for MCP49x1.) 167 | // // bit 14: buffer VREF? 168 | // // bit 13: gain bit; 0 for 1x gain, 1 for 2x (thus we NOT the variable) 169 | // // bit 12: shutdown bit. 1 for active operation 170 | // // bits 11 through 0: data 171 | // uint16_t out = (chan << 15) | (this->bufferVref << 14) 172 | // | ((!this->gain2x) << 13) | (1 << 12) 173 | // | (data << (12 - this->bitwidth)); 174 | } 175 | 176 | void MCP4X::write(unsigned int data) { 177 | // Drive chip select low 178 | if (MCP4X_PORT_WRITE) 179 | PORTB &= 0xfb; // Clear PORTB pin 2 = arduino pin 10 180 | else 181 | digitalWrite(ss_pin, LOW); 182 | 183 | // Send the command and data bits 184 | SPI.transfer((data & 0xff00) >> 8); 185 | SPI.transfer(data & 0xff); 186 | 187 | // Return chip select to high 188 | if (MCP4X_PORT_WRITE) 189 | PORTB |= (1 << 2); // set PORTB pin 2 = arduino pin 10 190 | else 191 | digitalWrite(ss_pin, HIGH); 192 | } 193 | 194 | 195 | // MCP49x2 (dual DAC) only. 196 | // Send a set of new values for the DAC to output in a single function call 197 | // These DACs have a function where you can change multiple DACs at the same 198 | // time: you call output() "sequentially", one DAC at a time, and *then*, 199 | // when they've all received the output data, pull the LDAC pin low on 200 | // all DACs at once. This function pulls the LDAC pin low for long enough 201 | // for the DAC(s) to change the output. 202 | // If this function is undesired, you can simply tie the LDAC pin to ground. 203 | // When tied to ground, you need *NOT* call this function! 204 | void MCP4X::latch(void) { 205 | // The datasheet says CS must return high for 40+ ns before this function 206 | // is called: no problems, that'd be taken care of automatically, as one 207 | // clock cycle at 16 MHz is longer... and there'll be a delay of multiple. 208 | 209 | if (LDAC_pin < 0) 210 | return; 211 | 212 | // We then need to hold LDAC low for at least 100 ns, i.e ~2 clock cycles. 213 | 214 | if (MCP4X_PORT_WRITE) { 215 | // This gives ~180 ns (three clock cycles, most of which is spent low) of 216 | // low time on a Uno R3 (16 MHz), measured on a scope to make sure 217 | PORTD &= ~(1 << 7); // Uno: digital pin 7; Mega: digital pin 38 218 | asm volatile("nop"); 219 | PORTD |= (1 << 7); 220 | } else { 221 | // This takes far, FAR longer than the above despite no NOP; digitalWrite 222 | // is SLOW! For comparison: the above takes 180 ns, this takes... 3.8 us, 223 | // or 3800 ns, 21 times as long - WITHOUT having a delay in there! 224 | digitalWrite(LDAC_pin, LOW); 225 | digitalWrite(LDAC_pin, HIGH); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /LaserShow/DAC_MCP4X.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Thomas Backman, 2012-07 (made a proper library 2011-07-30, 3 weeks after initial) 3 | * serenity@exscape.org 4 | * Support for MCP49x2 (MCP4902, MCP4912, MCP4922) added 2012-08-30, 5 | * with a patch and testing from Jonas Gruska 6 | 7 | * Code license: BSD/MIT. Whatever; I *prefer* to be credited, 8 | * and you're *not* allowed to claim you wrote this from scratch. 9 | * Apart from that, do whatever you feel like. Commercial projects, 10 | * code modifications, etc. 11 | 12 | * Gaftech 2012-11 13 | * Some customizations to fit my needs 14 | 15 | * Pins used: 16 | * Arduino pin 11 (for Uno; for Mega: 51) to device SDI (pin 4) - fixed pin 17 | * Arduino pin 13 (for Uno; for Mega: 52) to device SCK (pin 3) - fixed pin 18 | * Any digital pin to device LDAC (DAC pin 5) (except with PortWrite, see README) 19 | * Any digital pin to device CS (DAC pin 2) (as above) 20 | * 21 | * Other DAC wirings: 22 | * Pin 1: VDD, to +5 V 23 | * Pin 5: LDAC, either to an Arduino pin, or ground to update vout automatically 24 | * Pin 6: VREF, to +5 V (or some other reference voltage 0 < VREF <= VDD) 25 | * Pin 7: VSS, to ground 26 | * Pin 8: vout 27 | * (Pin 9, for the DFN package only: VSS) 28 | 29 | * Only tested on MCP4901 (8-bit) and MCP4922 (dual 12-bit), but it should work on the others as well. 30 | * Tested on an Arduino Uno R3. 31 | */ 32 | 33 | #ifndef _DAC_MCP4X_H 34 | #define _DAC_MCP4X_H 35 | 36 | #include 37 | #include 38 | 39 | // 40 | // Params 41 | // 42 | #define MCP4X_PORT_WRITE 1 43 | #define MCP4X_NO_LDAC -1 44 | #ifndef MCP4X_PORT_WRITE 45 | #define MCP4X_PORT_WRITE 0 46 | #endif 47 | #ifndef MCP4X_DEFAULTS 48 | #define MCP4X_DEFAULTS (MCP4X_BUFFERED | MCP4X_GAIN_1X | MCP4X_ACTIVE) 49 | #endif 50 | 51 | // 52 | // Constants 53 | // 54 | 55 | // Bits 56 | #define MCP4X_WRITE_B (1 << 15) 57 | #define MCP4X_BUFFERED (1 << 14) 58 | #define MCP4X_GAIN_1X (1 << 13) 59 | #define MCP4X_ACTIVE (1 << 12) 60 | 61 | // Channels 62 | #define MCP4X_CHAN_A 0 63 | #define MCP4X_CHAN_B 1 64 | 65 | // Model identifiers 66 | #define MCP4X_8_BITS 0 67 | #define MCP4X_10_BITS 1 68 | #define MCP4X_12_BITS 2 69 | #define MCP4X_INTREF (1 << 2) 70 | #define MCP4X_DUAL (1 << 3) 71 | 72 | // Model list (each model coded on 4 bits) 73 | #define MCP4X_4801 (MCP4X_8_BITS | MCP4X_INTREF) 74 | #define MCP4X_4811 (MCP4X_10_BITS | MCP4X_INTREF) 75 | #define MCP4X_4821 (MCP4X_12_BITS | MCP4X_INTREF) 76 | #define MCP4X_4802 (MCP4X_8_BITS | MCP4X_INTREF | MCP4X_DUAL) 77 | #define MCP4X_4812 (MCP4X_10_BITS | MCP4X_INTREF | MCP4X_DUAL) 78 | #define MCP4X_4822 (MCP4X_12_BITS | MCP4X_INTREF | MCP4X_DUAL) 79 | #define MCP4X_4901 (MCP4X_8_BITS) 80 | #define MCP4X_4911 (MCP4X_10_BITS) 81 | #define MCP4X_4921 (MCP4X_12_BITS) 82 | #define MCP4X_4902 (MCP4X_8_BITS | MCP4X_DUAL) 83 | #define MCP4X_4912 (MCP4X_10_BITS | MCP4X_DUAL) 84 | #define MCP4X_4922 (MCP4X_12_BITS | MCP4X_DUAL) 85 | 86 | class MCP4X { 87 | public: 88 | 89 | byte init(byte model, 90 | unsigned int vrefA = 5000, unsigned int vrefB = 5000, 91 | int ss_pin = SS, int ldac_pin = MCP4X_NO_LDAC, boolean autoLatch = 1); 92 | void begin(boolean beginSPI = 1); 93 | 94 | void configureSPI(); 95 | 96 | void setVref(byte chan, unsigned int vref) { vrefs[chan & 1] = vref; } 97 | void setVref(unsigned int vref) { vrefs[0] = vrefs[1] = vref; } 98 | void setBuffer(byte chan, boolean buffered); 99 | void setGain2x(byte chan, boolean gain2x = 1); 100 | void setAutoLatch(boolean enabled = 1) { autoLatch = enabled; } 101 | void shutdown(byte chan, boolean off = 1); 102 | 103 | void output(byte _chan, unsigned short _out); 104 | void output(unsigned short data) { output(MCP4X_CHAN_A, data); } 105 | ; 106 | /* same as output(), but having A/B makes more sense for dual DACs */ 107 | void outputA(unsigned short data) { output(MCP4X_CHAN_A, data); } 108 | void outputB(unsigned short data) { output(MCP4X_CHAN_B, data); } 109 | void output2(unsigned short _out, unsigned short _out2); // For MCP49x2 110 | 111 | // 112 | // output-like methods that takes a voltage argument 113 | // 114 | void setVoltage(byte chan, float v); 115 | 116 | int getGain(byte chan) { return regs[chan & 1] & MCP4X_GAIN_1X ? 1 : 2; } 117 | float getVoltageMV(byte chan); 118 | 119 | 120 | /* Actually change the output, if the LDAC pin isn't shorted to ground */ 121 | void latch(void); 122 | 123 | /* Only relevant for the MCP49x2 dual DACs. 124 | * If set, calling output2() will pull the LDAC pin low automatically, 125 | * which causes the output to change. 126 | * Not required if the LDAC pin is shorted to ground, however in that case, 127 | * there will be a delay between the updating of channel A and channel B. 128 | * If sync is desired, wire the LDAC pin to the Arduino and set this to true. 129 | */ 130 | void setAutomaticallyLatchDual(bool latch) { autoLatch = latch; }; 131 | 132 | private: 133 | unsigned int vrefs[2]; 134 | unsigned int regs[2]; 135 | boolean dual; 136 | int ss_pin; 137 | int LDAC_pin; 138 | int bitwidth; 139 | boolean autoLatch; /* call latch() automatically after output2() has been called? */ 140 | 141 | void write(unsigned int data); 142 | }; 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /LaserShow/Drawing.cpp: -------------------------------------------------------------------------------- 1 | // See LICENSE file for details 2 | // Copyright 2016 Florian Link (at) gmx.de 3 | #include "Drawing.h" 4 | #include "Font.h" 5 | 6 | 7 | void Drawing::drawString(String text, int x, int y, int count) 8 | { 9 | for (int loop = 0; loop < count; loop ++) 10 | { 11 | int i = 0; 12 | int x1 = x; 13 | while (text.charAt(i) != '\0') 14 | { 15 | x1 += drawLetter(text.charAt(i), x1, y); 16 | i ++; 17 | } 18 | } 19 | } 20 | 21 | long Drawing::stringAdvance(String text) 22 | { 23 | long adv = 0; 24 | int i = 0; 25 | while (text.charAt(i) != '\0') 26 | { 27 | adv += advance(text.charAt(i)); 28 | i ++; 29 | } 30 | return adv; 31 | } 32 | 33 | long Drawing::advance(byte letter) 34 | { 35 | long adv = 850; 36 | if (letter == 'I') { 37 | adv = 200; 38 | } else 39 | if (letter == 'W') { 40 | adv = 1000; 41 | } 42 | return adv; 43 | } 44 | 45 | long Drawing::drawLetter(byte letter, long translateX, long translateY) 46 | { 47 | long adv = advance(letter); 48 | 49 | switch (letter) 50 | { 51 | case 'A': drawObject(draw_A, sizeof(draw_A)/4, translateX, translateY); break; 52 | case 'B': drawObject(draw_B, sizeof(draw_B)/4, translateX, translateY); break; 53 | case 'C': drawObject(draw_C, sizeof(draw_C)/4, translateX, translateY); break; 54 | case 'D': drawObject(draw_D, sizeof(draw_D)/4, translateX, translateY); break; 55 | case 'E': drawObject(draw_E, sizeof(draw_E)/4, translateX, translateY); break; 56 | case 'F': drawObject(draw_F, sizeof(draw_F)/4, translateX, translateY); break; 57 | case 'G': drawObject(draw_G, sizeof(draw_G)/4, translateX, translateY); break; 58 | case 'H': drawObject(draw_H, sizeof(draw_H)/4, translateX, translateY); break; 59 | case 'I': drawObject(draw_I, sizeof(draw_I)/4, translateX, translateY); break; 60 | case 'J': drawObject(draw_J, sizeof(draw_J)/4, translateX, translateY); break; 61 | case 'K': drawObject(draw_K, sizeof(draw_K)/4, translateX, translateY); break; 62 | case 'L': drawObject(draw_L, sizeof(draw_L)/4, translateX, translateY); break; 63 | case 'M': drawObject(draw_M, sizeof(draw_M)/4, translateX, translateY); break; 64 | case 'N': drawObject(draw_N, sizeof(draw_N)/4, translateX, translateY); break; 65 | case 'O': drawObject(draw_O, sizeof(draw_O)/4, translateX, translateY); break; 66 | case 'P': drawObject(draw_P, sizeof(draw_P)/4, translateX, translateY); break; 67 | case 'Q': drawObject(draw_Q, sizeof(draw_Q)/4, translateX, translateY); break; 68 | case 'R': drawObject(draw_R, sizeof(draw_R)/4, translateX, translateY); break; 69 | case 'S': drawObject(draw_S, sizeof(draw_S)/4, translateX, translateY); break; 70 | case 'T': drawObject(draw_T, sizeof(draw_T)/4, translateX, translateY); break; 71 | case 'U': drawObject(draw_U, sizeof(draw_U)/4, translateX, translateY); break; 72 | case 'V': drawObject(draw_V, sizeof(draw_V)/4, translateX, translateY); break; 73 | case 'W': drawObject(draw_W, sizeof(draw_W)/4, translateX, translateY); break; 74 | case 'X': drawObject(draw_X, sizeof(draw_X)/4, translateX, translateY); break; 75 | case 'Y': drawObject(draw_Y, sizeof(draw_Y)/4, translateX, translateY); break; 76 | case 'Z': drawObject(draw_Z, sizeof(draw_Z)/4, translateX, translateY); break; 77 | 78 | case '0': drawObject(draw_0, sizeof(draw_0)/4, translateX, translateY); break; 79 | case '1': drawObject(draw_1, sizeof(draw_1)/4, translateX, translateY); break; 80 | case '2': drawObject(draw_2, sizeof(draw_2)/4, translateX, translateY); break; 81 | case '3': drawObject(draw_3, sizeof(draw_3)/4, translateX, translateY); break; 82 | case '4': drawObject(draw_4, sizeof(draw_4)/4, translateX, translateY); break; 83 | case '5': drawObject(draw_5, sizeof(draw_5)/4, translateX, translateY); break; 84 | case '6': drawObject(draw_6, sizeof(draw_6)/4, translateX, translateY); break; 85 | case '7': drawObject(draw_7, sizeof(draw_7)/4, translateX, translateY); break; 86 | case '8': drawObject(draw_8, sizeof(draw_8)/4, translateX, translateY); break; 87 | case '9': drawObject(draw_9, sizeof(draw_9)/4, translateX, translateY); break; 88 | case '!': drawObject(draw_exclam, sizeof(draw_exclam)/4, translateX, translateY); break; 89 | case '?': drawObject(draw_question, sizeof(draw_question)/4, translateX, translateY); break; 90 | case '.': drawObject(draw_dot, sizeof(draw_dot)/4, translateX, translateY); break; 91 | case ' ': 92 | break; 93 | 94 | } 95 | return adv; 96 | } 97 | 98 | void Drawing::drawObject(const unsigned short* data, int size, long translateX, long translateY) 99 | { 100 | const unsigned short* d = data; 101 | unsigned short posX; 102 | unsigned short posY; 103 | while (size>0) { 104 | posX = pgm_read_word(d); 105 | d++; 106 | posY = pgm_read_word(d); 107 | d++; 108 | size--; 109 | 110 | if (posX & 0x8000) { 111 | laser.on(); 112 | } else { 113 | laser.off(); 114 | } 115 | laser.sendto((posX & 0x7fff) + translateX, posY + translateY); 116 | } 117 | laser.off(); 118 | } 119 | 120 | long SIN(unsigned int angle); 121 | long COS(unsigned int angle); 122 | 123 | void Drawing::drawObjectRotated(const unsigned short* data, int size, long centerX, long centerY, int angle) 124 | { 125 | const unsigned short* d = data; 126 | unsigned short posX; 127 | unsigned short posY; 128 | while (size>0) { 129 | posX = pgm_read_word(d); 130 | d++; 131 | posY = pgm_read_word(d); 132 | d++; 133 | size--; 134 | 135 | if (posX & 0x8000) { 136 | laser.on(); 137 | } else { 138 | laser.off(); 139 | } 140 | FIXPT x = (long)(posX & 0x7fff) - centerX; 141 | FIXPT y = ((long)posY) - centerY; 142 | FIXPT x1 = COS(angle) * x - SIN(angle) * y; 143 | FIXPT y1 = COS(angle) * y + SIN(angle) * x; 144 | laser.sendto(TO_INT(x1), TO_INT(y1)); 145 | } 146 | laser.off(); 147 | } 148 | 149 | void Drawing::drawObjectRotated3D(const unsigned short* data, int size, long centerX, long centerY, int angleX, int angleY, int zDist) 150 | { 151 | Matrix3 world; 152 | Matrix3 tmp; 153 | tmp = Matrix3::rotateX(angleX); 154 | Matrix3::multiply(Matrix3::rotateY(angleY), tmp, world); 155 | 156 | laser.setEnable3D(true); 157 | laser.setMatrix(world); 158 | drawObject(data,size, -centerX, -centerY); 159 | laser.setEnable3D(false); 160 | } 161 | 162 | void Drawing::calcObjectBox(const unsigned short* data, int size, long& centerX, long& centerY, long& width, long& height) 163 | { 164 | const unsigned short* d = data; 165 | unsigned short posX; 166 | unsigned short posY; 167 | unsigned short x0 = 4096; 168 | unsigned short y0 = 4096; 169 | unsigned short x1 = 0; 170 | unsigned short y1 = 0; 171 | while (size>0) { 172 | posX = pgm_read_word(d) & 0x7fff; 173 | d++; 174 | posY = pgm_read_word(d); 175 | d++; 176 | size--; 177 | if (posX < x0) x0 = posX; 178 | if (posY < y0) y0 = posY; 179 | if (posX > x1) x1 = posX; 180 | if (posY > y1) y1 = posY; 181 | } 182 | centerX = (x0 + x1) / 2; 183 | centerY = (y0 + y1) / 2; 184 | width = x1 - x0; 185 | height = y1 - y0; 186 | } 187 | 188 | -------------------------------------------------------------------------------- /LaserShow/Drawing.h: -------------------------------------------------------------------------------- 1 | // See LICENSE file for details 2 | // Copyright 2016 Florian Link (at) gmx.de 3 | #ifndef DRAWING_H 4 | #define DRAWING_H 5 | 6 | #include "Laser.h" 7 | 8 | extern Laser laser; 9 | 10 | //! Allows to draw text and objects from PROGMEM. 11 | class Drawing 12 | { 13 | public: 14 | //! Draws the given string at x,y position. Count indicates how often the drawing is repeated. 15 | static void drawString(String text, int x, int y, int repeat = 1); 16 | 17 | //! Draws the given letter (A-Z, 0-9, !? are currently supported in the font), returns the x advance... 18 | static long drawLetter(byte letter, long translateX = 0, long translateY = 0); 19 | 20 | //! Get X advance for given char 21 | static long advance(byte letter); 22 | 23 | //! Get X advance for string 24 | static long stringAdvance(String text); 25 | 26 | //! Draws the given data (which needs to be in PROGMEM). Size indicates the number 27 | //! of draw commands (so it is sizeof(data)/4). 28 | static void drawObject(const unsigned short* data, int size, long translateX = 0, long translateY = 0); 29 | 30 | //! Draws the given data (which needs to be in PROGMEM). Size indicates the number 31 | //! of draw commands (so it is sizeof(data)/4). 32 | static void drawObjectRotated(const unsigned short* data, int size, long centerX, long centerY, int angle); 33 | 34 | //! Draws the object and rotates in 3D. 35 | static void drawObjectRotated3D(const unsigned short* data, int size, long centerX, long centerY, int angleX, int angleY, int fov); 36 | 37 | //! Returns the center of the object (center of bounding box) 38 | static void calcObjectBox(const unsigned short* data, int size, long& centerX, long& centerY, long& width, long& height); 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /LaserShow/Font.h: -------------------------------------------------------------------------------- 1 | #ifndef FONT_H 2 | #define FONT_H 3 | 4 | // Font converted from gcode generated with free stick fonts at: 5 | // http://ncplot.com/stickfont/stickfont.htm 6 | 7 | // To save memory, the move commands are stores in PROGMEM. 8 | // Each move command consists of two 16bit shorts (one for x, one for y) 9 | // The highest bit (0x8000) of x indicates if the laser should be on 10 | // and needs to be masked by the drawing code. 11 | 12 | const unsigned short draw_A[] PROGMEM = { 13 | 0x17c,0x3e8, 14 | 0x8000,0x0, 15 | 0x17c,0x3e8, 16 | 0x82f9,0x0, 17 | 0x8e,0x14d, 18 | 0x826b,0x14d, 19 | }; 20 | const unsigned short draw_B[] PROGMEM = { 21 | 0x0,0x3e8, 22 | 0x8000,0x0, 23 | 0x0,0x3e8, 24 | 0x81ac,0x3e8, 25 | 0x823b,0x3b8, 26 | 0x826b,0x388, 27 | 0x829a,0x329, 28 | 0x829a,0x2ca, 29 | 0x826b,0x26b, 30 | 0x823b,0x23b, 31 | 0x81ac,0x20b, 32 | 0x0,0x20b, 33 | 0x81ac,0x20b, 34 | 0x823b,0x1dc, 35 | 0x826b,0x1ac, 36 | 0x829a,0x14d, 37 | 0x829a,0xbe, 38 | 0x826b,0x5f, 39 | 0x823b,0x2f, 40 | 0x81ac,0x0, 41 | 0x8000,0x0, 42 | }; 43 | const unsigned short draw_C[] PROGMEM = { 44 | 0x2ca,0x2f9, 45 | 0x829a,0x359, 46 | 0x823b,0x3b8, 47 | 0x81dc,0x3e8, 48 | 0x811d,0x3e8, 49 | 0x80be,0x3b8, 50 | 0x805f,0x359, 51 | 0x802f,0x2f9, 52 | 0x8000,0x26b, 53 | 0x8000,0x17c, 54 | 0x802f,0xee, 55 | 0x805f,0x8e, 56 | 0x80be,0x2f, 57 | 0x811d,0x0, 58 | 0x81dc,0x0, 59 | 0x823b,0x2f, 60 | 0x829a,0x8e, 61 | 0x82ca,0xee, 62 | }; 63 | const unsigned short draw_D[] PROGMEM = { 64 | 0x0,0x3e8, 65 | 0x8000,0x0, 66 | 0x0,0x3e8, 67 | 0x814d,0x3e8, 68 | 0x81dc,0x3b8, 69 | 0x823b,0x359, 70 | 0x826b,0x2f9, 71 | 0x829a,0x26b, 72 | 0x829a,0x17c, 73 | 0x826b,0xee, 74 | 0x823b,0x8e, 75 | 0x81dc,0x2f, 76 | 0x814d,0x0, 77 | 0x8000,0x0, 78 | }; 79 | const unsigned short draw_E[] PROGMEM = { 80 | 0x0,0x3e8, 81 | 0x8000,0x0, 82 | 0x0,0x3e8, 83 | 0x826b,0x3e8, 84 | 0x0,0x20b, 85 | 0x817c,0x20b, 86 | 0x0,0x0, 87 | 0x826b,0x0, 88 | }; 89 | const unsigned short draw_F[] PROGMEM = { 90 | 0x0,0x3e8, 91 | 0x8000,0x0, 92 | 0x0,0x3e8, 93 | 0x826b,0x3e8, 94 | 0x0,0x20b, 95 | 0x817c,0x20b, 96 | }; 97 | const unsigned short draw_G[] PROGMEM = { 98 | 0x2ca,0x2f9, 99 | 0x829a,0x359, 100 | 0x823b,0x3b8, 101 | 0x81dc,0x3e8, 102 | 0x811d,0x3e8, 103 | 0x80be,0x3b8, 104 | 0x805f,0x359, 105 | 0x802f,0x2f9, 106 | 0x8000,0x26b, 107 | 0x8000,0x17c, 108 | 0x802f,0xee, 109 | 0x805f,0x8e, 110 | 0x80be,0x2f, 111 | 0x811d,0x0, 112 | 0x81dc,0x0, 113 | 0x823b,0x2f, 114 | 0x829a,0x8e, 115 | 0x82ca,0xee, 116 | 0x82ca,0x17c, 117 | 0x1dc,0x17c, 118 | 0x82ca,0x17c, 119 | }; 120 | const unsigned short draw_H[] PROGMEM = { 121 | 0x0,0x3e8, 122 | 0x8000,0x0, 123 | 0x29a,0x3e8, 124 | 0x829a,0x0, 125 | 0x0,0x20b, 126 | 0x829a,0x20b, 127 | }; 128 | const unsigned short draw_I[] PROGMEM = { 129 | 0x0,0x3e8, 130 | 0x8000,0x0, 131 | }; 132 | const unsigned short draw_J[] PROGMEM = { 133 | 0x1dc,0x3e8, 134 | 0x81dc,0xee, 135 | 0x81ac,0x5f, 136 | 0x817c,0x2f, 137 | 0x811d,0x0, 138 | 0x80be,0x0, 139 | 0x805f,0x2f, 140 | 0x802f,0x5f, 141 | 0x8000,0xee, 142 | 0x8000,0x14d, 143 | }; 144 | const unsigned short draw_K[] PROGMEM = { 145 | 0x0,0x3e8, 146 | 0x8000,0x0, 147 | 0x29a,0x3e8, 148 | 0x8000,0x14d, 149 | 0xee,0x23b, 150 | 0x829a,0x0, 151 | }; 152 | const unsigned short draw_L[] PROGMEM = { 153 | 0x0,0x3e8, 154 | 0x8000,0x0, 155 | 0x823b,0x0, 156 | }; 157 | const unsigned short draw_M[] PROGMEM = { 158 | 0x0,0x3e8, 159 | 0x8000,0x0, 160 | 0x0,0x3e8, 161 | 0x817c,0x0, 162 | 0x2f9,0x3e8, 163 | 0x817c,0x0, 164 | 0x2f9,0x3e8, 165 | 0x82f9,0x0, 166 | }; 167 | const unsigned short draw_N[] PROGMEM = { 168 | 0x0,0x3e8, 169 | 0x8000,0x0, 170 | 0x0,0x3e8, 171 | 0x829a,0x0, 172 | 0x29a,0x3e8, 173 | 0x829a,0x0, 174 | }; 175 | const unsigned short draw_O[] PROGMEM = { 176 | 0x11d,0x3e8, 177 | 0x80be,0x3b8, 178 | 0x805f,0x359, 179 | 0x802f,0x2f9, 180 | 0x8000,0x26b, 181 | 0x8000,0x17c, 182 | 0x802f,0xee, 183 | 0x805f,0x8e, 184 | 0x80be,0x2f, 185 | 0x811d,0x0, 186 | 0x81dc,0x0, 187 | 0x823b,0x2f, 188 | 0x829a,0x8e, 189 | 0x82ca,0xee, 190 | 0x82f9,0x17c, 191 | 0x82f9,0x26b, 192 | 0x82ca,0x2f9, 193 | 0x829a,0x359, 194 | 0x823b,0x3b8, 195 | 0x81dc,0x3e8, 196 | 0x811d,0x3e8, 197 | }; 198 | const unsigned short draw_P[] PROGMEM = { 199 | 0x0,0x3e8, 200 | 0x8000,0x0, 201 | 0x0,0x3e8, 202 | 0x81ac,0x3e8, 203 | 0x823b,0x3b8, 204 | 0x826b,0x388, 205 | 0x829a,0x329, 206 | 0x829a,0x29a, 207 | 0x826b,0x23b, 208 | 0x823b,0x20b, 209 | 0x81ac,0x1dc, 210 | 0x8000,0x1dc, 211 | }; 212 | const unsigned short draw_Q[] PROGMEM = { 213 | 0x11d,0x447, 214 | 0x80be,0x417, 215 | 0x805f,0x3b8, 216 | 0x802f,0x359, 217 | 0x8000,0x2ca, 218 | 0x8000,0x1dc, 219 | 0x802f,0x14d, 220 | 0x805f,0xee, 221 | 0x80be,0x8e, 222 | 0x811d,0x5f, 223 | 0x81dc,0x5f, 224 | 0x823b,0x8e, 225 | 0x829a,0xee, 226 | 0x82ca,0x14d, 227 | 0x82f9,0x1dc, 228 | 0x82f9,0x2ca, 229 | 0x82ca,0x359, 230 | 0x829a,0x3b8, 231 | 0x823b,0x417, 232 | 0x81dc,0x447, 233 | 0x811d,0x447, 234 | 0x1ac,0x11d, 235 | 0x82ca,0x0, 236 | }; 237 | const unsigned short draw_R[] PROGMEM = { 238 | 0x0,0x3e8, 239 | 0x8000,0x0, 240 | 0x0,0x3e8, 241 | 0x81ac,0x3e8, 242 | 0x823b,0x3b8, 243 | 0x826b,0x388, 244 | 0x829a,0x329, 245 | 0x829a,0x2ca, 246 | 0x826b,0x26b, 247 | 0x823b,0x23b, 248 | 0x81ac,0x20b, 249 | 0x8000,0x20b, 250 | 0x14d,0x20b, 251 | 0x829a,0x0, 252 | }; 253 | const unsigned short draw_S[] PROGMEM = { 254 | 0x29a,0x359, 255 | 0x823b,0x3b8, 256 | 0x81ac,0x3e8, 257 | 0x80ee,0x3e8, 258 | 0x805f,0x3b8, 259 | 0x8000,0x359, 260 | 0x8000,0x2f9, 261 | 0x802f,0x29a, 262 | 0x805f,0x26b, 263 | 0x80be,0x23b, 264 | 0x81dc,0x1dc, 265 | 0x823b,0x1ac, 266 | 0x826b,0x17c, 267 | 0x829a,0x11d, 268 | 0x829a,0x8e, 269 | 0x823b,0x2f, 270 | 0x81ac,0x0, 271 | 0x80ee,0x0, 272 | 0x805f,0x2f, 273 | 0x8000,0x8e, 274 | }; 275 | const unsigned short draw_T[] PROGMEM = { 276 | 0x14d,0x3e8, 277 | 0x814d,0x0, 278 | 0x0,0x3e8, 279 | 0x829a,0x3e8, 280 | }; 281 | const unsigned short draw_U[] PROGMEM = { 282 | 0x0,0x3e8, 283 | 0x8000,0x11d, 284 | 0x802f,0x8e, 285 | 0x808e,0x2f, 286 | 0x811d,0x0, 287 | 0x817c,0x0, 288 | 0x820b,0x2f, 289 | 0x826b,0x8e, 290 | 0x829a,0x11d, 291 | 0x829a,0x3e8, 292 | }; 293 | const unsigned short draw_V[] PROGMEM = { 294 | 0x0,0x3e8, 295 | 0x817c,0x0, 296 | 0x2f9,0x3e8, 297 | 0x817c,0x0, 298 | }; 299 | const unsigned short draw_W[] PROGMEM = { 300 | 0x0,0x3e8, 301 | 0x80ee,0x0, 302 | 0x1dc,0x3e8, 303 | 0x80ee,0x0, 304 | 0x1dc,0x3e8, 305 | 0x82ca,0x0, 306 | 0x3b8,0x3e8, 307 | 0x82ca,0x0, 308 | }; 309 | const unsigned short draw_X[] PROGMEM = { 310 | 0x0,0x3e8, 311 | 0x829a,0x0, 312 | 0x29a,0x3e8, 313 | 0x8000,0x0, 314 | }; 315 | const unsigned short draw_Y[] PROGMEM = { 316 | 0x0,0x3e8, 317 | 0x817c,0x20b, 318 | 0x817c,0x0, 319 | 0x2f9,0x3e8, 320 | 0x817c,0x20b, 321 | }; 322 | const unsigned short draw_Z[] PROGMEM = { 323 | 0x29a,0x3e8, 324 | 0x8000,0x0, 325 | 0x0,0x3e8, 326 | 0x829a,0x3e8, 327 | 0x0,0x0, 328 | 0x829a,0x0, 329 | }; 330 | const unsigned short draw_0[] PROGMEM = { 331 | 0x11d,0x3e8, 332 | 0x808e,0x3b8, 333 | 0x802f,0x329, 334 | 0x8000,0x23b, 335 | 0x8000,0x1ac, 336 | 0x802f,0xbe, 337 | 0x808e,0x2f, 338 | 0x811d,0x0, 339 | 0x817c,0x0, 340 | 0x820b,0x2f, 341 | 0x826b,0xbe, 342 | 0x829a,0x1ac, 343 | 0x829a,0x23b, 344 | 0x826b,0x329, 345 | 0x820b,0x3b8, 346 | 0x817c,0x3e8, 347 | 0x811d,0x3e8, 348 | }; 349 | const unsigned short draw_1[] PROGMEM = { 350 | 0x0,0x329, 351 | 0x805f,0x359, 352 | 0x80ee,0x3e8, 353 | 0x80ee,0x0, 354 | }; 355 | const unsigned short draw_2[] PROGMEM = { 356 | 0x2f,0x2f9, 357 | 0x802f,0x329, 358 | 0x805f,0x388, 359 | 0x808e,0x3b8, 360 | 0x80ee,0x3e8, 361 | 0x81ac,0x3e8, 362 | 0x820b,0x3b8, 363 | 0x823b,0x388, 364 | 0x826b,0x329, 365 | 0x826b,0x2ca, 366 | 0x823b,0x26b, 367 | 0x81dc,0x1dc, 368 | 0x8000,0x0, 369 | 0x829a,0x0, 370 | }; 371 | const unsigned short draw_3[] PROGMEM = { 372 | 0x5f,0x3e8, 373 | 0x826b,0x3e8, 374 | 0x814d,0x26b, 375 | 0x81dc,0x26b, 376 | 0x823b,0x23b, 377 | 0x826b,0x20b, 378 | 0x829a,0x17c, 379 | 0x829a,0x11d, 380 | 0x826b,0x8e, 381 | 0x820b,0x2f, 382 | 0x817c,0x0, 383 | 0x80ee,0x0, 384 | 0x805f,0x2f, 385 | 0x802f,0x5f, 386 | 0x8000,0xbe, 387 | }; 388 | const unsigned short draw_4[] PROGMEM = { 389 | 0x1dc,0x3e8, 390 | 0x8000,0x14d, 391 | 0x82ca,0x14d, 392 | 0x1dc,0x3e8, 393 | 0x81dc,0x0, 394 | }; 395 | const unsigned short draw_5[] PROGMEM = { 396 | 0x23b,0x3e8, 397 | 0x805f,0x3e8, 398 | 0x802f,0x23b, 399 | 0x805f,0x26b, 400 | 0x80ee,0x29a, 401 | 0x817c,0x29a, 402 | 0x820b,0x26b, 403 | 0x826b,0x20b, 404 | 0x829a,0x17c, 405 | 0x829a,0x11d, 406 | 0x826b,0x8e, 407 | 0x820b,0x2f, 408 | 0x817c,0x0, 409 | 0x80ee,0x0, 410 | 0x805f,0x2f, 411 | 0x802f,0x5f, 412 | 0x8000,0xbe, 413 | }; 414 | const unsigned short draw_6[] PROGMEM = { 415 | 0x23b,0x359, 416 | 0x820b,0x3b8, 417 | 0x817c,0x3e8, 418 | 0x811d,0x3e8, 419 | 0x808e,0x3b8, 420 | 0x802f,0x329, 421 | 0x8000,0x23b, 422 | 0x8000,0x14d, 423 | 0x802f,0x8e, 424 | 0x808e,0x2f, 425 | 0x811d,0x0, 426 | 0x814d,0x0, 427 | 0x81dc,0x2f, 428 | 0x823b,0x8e, 429 | 0x826b,0x11d, 430 | 0x826b,0x14d, 431 | 0x823b,0x1dc, 432 | 0x81dc,0x23b, 433 | 0x814d,0x26b, 434 | 0x811d,0x26b, 435 | 0x808e,0x23b, 436 | 0x802f,0x1dc, 437 | 0x8000,0x14d, 438 | }; 439 | const unsigned short draw_7[] PROGMEM = { 440 | 0x29a,0x3e8, 441 | 0x80be,0x0, 442 | 0x0,0x3e8, 443 | 0x829a,0x3e8, 444 | }; 445 | const unsigned short draw_8[] PROGMEM = { 446 | 0xee,0x3e8, 447 | 0x805f,0x3b8, 448 | 0x802f,0x359, 449 | 0x802f,0x2f9, 450 | 0x805f,0x29a, 451 | 0x80be,0x26b, 452 | 0x817c,0x23b, 453 | 0x820b,0x20b, 454 | 0x826b,0x1ac, 455 | 0x829a,0x14d, 456 | 0x829a,0xbe, 457 | 0x826b,0x5f, 458 | 0x823b,0x2f, 459 | 0x81ac,0x0, 460 | 0x80ee,0x0, 461 | 0x805f,0x2f, 462 | 0x802f,0x5f, 463 | 0x8000,0xbe, 464 | 0x8000,0x14d, 465 | 0x802f,0x1ac, 466 | 0x808e,0x20b, 467 | 0x811d,0x23b, 468 | 0x81dc,0x26b, 469 | 0x823b,0x29a, 470 | 0x826b,0x2f9, 471 | 0x826b,0x359, 472 | 0x823b,0x3b8, 473 | 0x81ac,0x3e8, 474 | 0x80ee,0x3e8, 475 | }; 476 | const unsigned short draw_9[] PROGMEM = { 477 | 0x26b,0x29a, 478 | 0x823b,0x20b, 479 | 0x81dc,0x1ac, 480 | 0x814d,0x17c, 481 | 0x811d,0x17c, 482 | 0x808e,0x1ac, 483 | 0x802f,0x20b, 484 | 0x8000,0x29a, 485 | 0x8000,0x2ca, 486 | 0x802f,0x359, 487 | 0x808e,0x3b8, 488 | 0x811d,0x3e8, 489 | 0x814d,0x3e8, 490 | 0x81dc,0x3b8, 491 | 0x823b,0x359, 492 | 0x826b,0x29a, 493 | 0x826b,0x1ac, 494 | 0x823b,0xbe, 495 | 0x81dc,0x2f, 496 | 0x814d,0x0, 497 | 0x80ee,0x0, 498 | 0x805f,0x2f, 499 | 0x802f,0x8e, 500 | }; 501 | const unsigned short draw_exclam[] PROGMEM = { 502 | 0x2f,0x3e8, 503 | 0x802f,0x14d, 504 | 0x2f,0x5f, 505 | 0x8000,0x2f, 506 | 0x802f,0x0, 507 | 0x805f,0x2f, 508 | 0x802f,0x5f, 509 | }; 510 | 511 | const unsigned short draw_dot[] PROGMEM = { 512 | 0x11d,0x5f, 513 | 0x80ee,0x2f, 514 | 0x811d,0x0, 515 | 0x814d,0x2f, 516 | 0x811d,0x5f, 517 | }; 518 | 519 | const unsigned short draw_question[] PROGMEM = { 520 | 0x0,0x2f9, 521 | 0x8000,0x329, 522 | 0x802f,0x388, 523 | 0x805f,0x3b8, 524 | 0x80be,0x3e8, 525 | 0x817c,0x3e8, 526 | 0x81dc,0x3b8, 527 | 0x820b,0x388, 528 | 0x823b,0x329, 529 | 0x823b,0x2ca, 530 | 0x820b,0x26b, 531 | 0x81dc,0x23b, 532 | 0x811d,0x1dc, 533 | 0x811d,0x14d, 534 | 0x11d,0x5f, 535 | 0x80ee,0x2f, 536 | 0x811d,0x0, 537 | 0x814d,0x2f, 538 | 0x811d,0x5f, 539 | }; 540 | const unsigned short draw_slash[] PROGMEM = { 541 | 0x359,0x5f3, 542 | 0x8000,0x0, 543 | }; 544 | const unsigned short draw_open[] PROGMEM = { 545 | 0x0,0x5f3, 546 | 0x8000,0x0, 547 | 0x2f,0x5f3, 548 | 0x802f,0x0, 549 | 0x0,0x5f3, 550 | 0x814d,0x5f3, 551 | 0x0,0x0, 552 | 0x814d,0x0, 553 | }; 554 | const unsigned short draw_close[] PROGMEM = { 555 | 0x11d,0x5f3, 556 | 0x811d,0x0, 557 | 0x14d,0x5f3, 558 | 0x814d,0x0, 559 | 0x0,0x5f3, 560 | 0x814d,0x5f3, 561 | 0x0,0x0, 562 | 0x814d,0x0, 563 | }; 564 | #endif 565 | -------------------------------------------------------------------------------- /LaserShow/Laser.cpp: -------------------------------------------------------------------------------- 1 | // See LICENSE file for details 2 | // Copyright 2016 Florian Link (at) gmx.de 3 | #include "Laser.h" 4 | 5 | // these values can be adapted to fine tune sendto: 6 | 7 | // if this is enabled, pins need to be 10 and 7 in dac init below, but it is a big speedup! 8 | #define MCP4X_PORT_WRITE 1 9 | 10 | #include "DAC_MCP4X.h" 11 | 12 | MCP4X dac; 13 | 14 | Laser::Laser(int laserPin) 15 | { 16 | _laserPin = laserPin; 17 | _quality = FROM_FLOAT(1./(LASER_QUALITY)); 18 | 19 | _x = 0; 20 | _y = 0; 21 | _oldX = 0; 22 | _oldY = 0; 23 | 24 | _state = 0; 25 | 26 | _scale = 1; 27 | _offsetX = 0; 28 | _offsetY = 0; 29 | 30 | _moved = 0; 31 | _maxMove = -1; 32 | _laserForceOff = false; 33 | resetClipArea(); 34 | 35 | _enable3D = false; 36 | _zDist = 1000; 37 | } 38 | 39 | void Laser::init() 40 | { 41 | dac.init(MCP4X_4822, 5000, 5000, 42 | 10, 7, 1); 43 | dac.setGain2x(MCP4X_CHAN_A, 0); 44 | dac.setGain2x(MCP4X_CHAN_B, 0); 45 | dac.begin(1); 46 | 47 | pinMode(_laserPin, OUTPUT); 48 | } 49 | 50 | void Laser::sendToDAC(int x, int y) 51 | { 52 | #ifdef LASER_SWAP_XY 53 | int x1 = y; 54 | int y1 = x; 55 | #else 56 | int x1 = x; 57 | int y1 = y; 58 | #endif 59 | #ifdef LASER_FLIP_X 60 | x1 = 4095 - x1; 61 | #endif 62 | #ifdef LASER_FLIP_Y 63 | y1 = 4095 - y1; 64 | #endif 65 | dac.output2(x1, y1); 66 | } 67 | 68 | void Laser::resetClipArea() 69 | { 70 | _clipXMin = 0; 71 | _clipYMin = 0; 72 | _clipXMax = 4095; 73 | _clipYMax = 4095; 74 | } 75 | 76 | void Laser::setClipArea(long x, long y, long x1, long y1) 77 | { 78 | _clipXMin = x; 79 | _clipYMin = y; 80 | _clipXMax = x1; 81 | _clipYMax = y1; 82 | } 83 | 84 | const int INSIDE = 0; // 0000 85 | const int LEFT = 1; // 0001 86 | const int RIGHT = 2; // 0010 87 | const int BOTTOM = 4; // 0100 88 | const int TOP = 8; // 1000 89 | 90 | int Laser::computeOutCode(long x, long y) 91 | { 92 | int code = INSIDE; // initialised as being inside of [[clip window]] 93 | 94 | if (x < _clipXMin) // to the left of clip window 95 | code |= LEFT; 96 | else if (x > _clipXMax) // to the right of clip window 97 | code |= RIGHT; 98 | if (y < _clipYMin) // below the clip window 99 | code |= BOTTOM; 100 | else if (y > _clipYMax) // above the clip window 101 | code |= TOP; 102 | 103 | return code; 104 | } 105 | 106 | // Cohen–Sutherland clipping algorithm clips a line from 107 | // P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with 108 | // diagonal from (_clipXMin, _clipYMin) to (_clipXMax, _clipYMax). 109 | bool Laser::clipLine(long& x0, long& y0, long& x1, long& y1) 110 | { 111 | // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle 112 | int outcode0 = computeOutCode(x0, y0); 113 | int outcode1 = computeOutCode(x1, y1); 114 | bool accept = false; 115 | 116 | while (true) { 117 | if (!(outcode0 | outcode1)) { // Bitwise OR is 0. Trivially accept and get out of loop 118 | accept = true; 119 | break; 120 | } else if (outcode0 & outcode1) { // Bitwise AND is not 0. Trivially reject and get out of loop 121 | break; 122 | } else { 123 | // failed both tests, so calculate the line segment to clip 124 | // from an outside point to an intersection with clip edge 125 | long x, y; 126 | 127 | // At least one endpoint is outside the clip rectangle; pick it. 128 | int outcodeOut = outcode0 ? outcode0 : outcode1; 129 | 130 | // Now find the intersection point; 131 | // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0) 132 | if (outcodeOut & TOP) { // point is above the clip rectangle 133 | x = x0 + (x1 - x0) * float(_clipYMax - y0) / float(y1 - y0); 134 | y = _clipYMax; 135 | } else if (outcodeOut & BOTTOM) { // point is below the clip rectangle 136 | x = x0 + (x1 - x0) * float(_clipYMin - y0) / float(y1 - y0); 137 | y = _clipYMin; 138 | } else if (outcodeOut & RIGHT) { // point is to the right of clip rectangle 139 | y = y0 + (y1 - y0) * float(_clipXMax - x0) / float(x1 - x0); 140 | x = _clipXMax; 141 | } else if (outcodeOut & LEFT) { // point is to the left of clip rectangle 142 | y = y0 + (y1 - y0) * float(_clipXMin - x0) / float(x1 - x0); 143 | x = _clipXMin; 144 | } 145 | 146 | // Now we move outside point to intersection point to clip 147 | // and get ready for next pass. 148 | if (outcodeOut == outcode0) { 149 | x0 = x; 150 | y0 = y; 151 | outcode0 = computeOutCode(x0, y0); 152 | } else { 153 | x1 = x; 154 | y1 = y; 155 | outcode1 = computeOutCode(x1, y1); 156 | } 157 | } 158 | } 159 | return accept; 160 | } 161 | 162 | void Laser::sendto (long xpos, long ypos) 163 | { 164 | if (_enable3D) { 165 | Vector3i p1; 166 | Vector3i p; 167 | p1.x = xpos; 168 | p1.y = ypos; 169 | p1.z = 0; 170 | Matrix3::applyMatrix(_matrix, p1, p); 171 | xpos = ((_zDist*(long)p.x) / (_zDist + (long)p.z)) + 2048; 172 | ypos = ((_zDist*(long)p.y) / (_zDist + (long)p.z)) + 2048; 173 | } 174 | // Float was too slow on Arduino, so I used 175 | // fixed point precision here: 176 | long xNew = TO_INT(xpos * _scale) + _offsetX; 177 | long yNew = TO_INT(ypos * _scale) + _offsetY; 178 | long clipX = xNew; 179 | long clipY = yNew; 180 | long oldX = _oldX; 181 | long oldY = _oldY; 182 | if (clipLine(oldX,oldY, clipX,clipY)) { 183 | if (oldX != _oldX || oldY != _oldY) { 184 | sendtoRaw(oldX, oldY); 185 | } 186 | sendtoRaw(clipX, clipY); 187 | } 188 | _oldX = xNew; 189 | _oldY = yNew; 190 | } 191 | 192 | void Laser::sendtoRaw (long xNew, long yNew) 193 | { 194 | // devide into equal parts, using _quality 195 | long fdiffx = xNew - _x; 196 | long fdiffy = yNew - _y; 197 | long diffx = TO_INT(abs(fdiffx) * _quality); 198 | long diffy = TO_INT(abs(fdiffy) * _quality); 199 | 200 | // store movement for max move 201 | long moved = _moved; 202 | _moved += abs(fdiffx) + abs(fdiffy); 203 | 204 | // use the bigger direction 205 | if (diffx < diffy) 206 | { 207 | diffx = diffy; 208 | } 209 | fdiffx = FROM_INT(fdiffx) / diffx; 210 | fdiffy = FROM_INT(fdiffy) / diffx; 211 | // interpolate in FIXPT 212 | FIXPT tmpx = 0; 213 | FIXPT tmpy = 0; 214 | for (int i = 0; i _maxMove) { 220 | off(); 221 | _laserForceOff = true; 222 | _maxMoveX = _x + TO_INT(tmpx); 223 | _maxMoveY = _y + TO_INT(tmpy); 224 | } 225 | } 226 | tmpx += fdiffx; 227 | tmpy += fdiffy; 228 | sendToDAC(_x + TO_INT(tmpx), _y + TO_INT(tmpy)); 229 | #ifdef LASER_MOVE_DELAY 230 | wait(LASER_MOVE_DELAY); 231 | #endif 232 | } 233 | 234 | // for max move, stop if required... 235 | if (!_laserForceOff && _maxMove != -1 && _moved > _maxMove) { 236 | off(); 237 | _laserForceOff = true; 238 | _maxMoveX = xNew; 239 | _maxMoveY = yNew; 240 | } 241 | 242 | _x = xNew; 243 | _y = yNew; 244 | sendToDAC(_x, _y); 245 | wait(LASER_END_DELAY); 246 | } 247 | 248 | void Laser::drawline(long x1, long y1, long x2, long y2) 249 | { 250 | if (_x != x1 or _y != y1) 251 | { 252 | off(); 253 | sendto(x1,y1); 254 | } 255 | on(); 256 | sendto(x2,y2); 257 | wait(LASER_LINE_END_DELAY); 258 | } 259 | 260 | void Laser::on() 261 | { 262 | if (!_state && !_laserForceOff) 263 | { 264 | wait(LASER_TOGGLE_DELAY); 265 | _state = 1; 266 | digitalWrite(_laserPin, HIGH); 267 | } 268 | } 269 | 270 | void Laser::off() 271 | { 272 | if (_state) 273 | { 274 | wait(LASER_TOGGLE_DELAY); 275 | _state = 0; 276 | digitalWrite(_laserPin, LOW); 277 | } 278 | } 279 | 280 | void Laser::wait(long length) 281 | { 282 | delayMicroseconds(length); 283 | } 284 | 285 | void Laser::setScale(float scale) 286 | { 287 | _scale = FROM_FLOAT(scale); 288 | } 289 | 290 | void Laser::setOffset(long offsetX, long offsetY) 291 | { 292 | _offsetX = offsetX; 293 | _offsetY = offsetY; 294 | } 295 | 296 | -------------------------------------------------------------------------------- /LaserShow/Laser.h: -------------------------------------------------------------------------------- 1 | // See LICENSE file for details 2 | // Copyright 2016 Florian Link (at) gmx.de 3 | #ifndef LASER_H 4 | #define LASER_H 5 | 6 | #include "Arduino.h" 7 | #include "Basics.h" 8 | 9 | // -- The following flags can be used to fine tune the laser timing 10 | 11 | // defines the granularity of the line interpolation. 64 means that each line is split into steps of 64 pixels in the longer direction. 12 | // setting smaller values will slow down the rendering but will cause more linearity in the galvo movement, 13 | // setting bigger values will cause faster rendering, but lines will not be straight anymore. 14 | #define LASER_QUALITY 64 15 | 16 | // Defines how long the galvos wait for the on/off toggling of the laser pointer (in microseconds), this will depend on your laser pointer. 17 | #define LASER_TOGGLE_DELAY 500 18 | // Defines how long the galvos wait at the end of a line (currently only used for the 3D cube rendering, in microseconds). 19 | #define LASER_LINE_END_DELAY 200 20 | // Defines the delay the laser waits after reaching a given position (in microseconds). 21 | #define LASER_END_DELAY 5 22 | // Defines the delay after each laser movement (used when interpolating lines, in microseconds), if not defines, 0 is used 23 | //#define LASER_MOVE_DELAY 5 24 | 25 | // -- The following flags can be used to rotate/flip the output without changing the DAC wiring, just uncomment the desired swap/flip 26 | // define this to swap X and Y on the DAC 27 | #define LASER_SWAP_XY 28 | // define this to flip along the x axis 29 | #define LASER_FLIP_X 30 | // define this to flip along the y axis 31 | #define LASER_FLIP_Y 32 | 33 | //! Encapsulates the laser movement and on/off state. 34 | class Laser 35 | { 36 | public: 37 | //! The laser is initialized with the laserPin, 38 | //! which selects the digital pin that turns the laser pointer on/off. 39 | Laser(int laserPin); 40 | 41 | void init(); 42 | 43 | //! send the laser to the given position, scaled and translated and line clipped. 44 | void sendto(long x, long y); 45 | //! sends the laser to the raw position (the movement is always linearly interpolated depending on the quality, 46 | //! to avoid too fast movements. 47 | void sendtoRaw(long x, long y); 48 | 49 | //! draws a line by turning the laser off, going to x1,y1, turning it on and going to x2,y2. 50 | void drawline(long x1, long y1, long x2, long y2); 51 | 52 | void wait(long length); 53 | 54 | void on(); 55 | void off(); 56 | 57 | void setScale(float scale); 58 | void setOffset(long offsetX, long offsetY); 59 | 60 | void resetClipArea(); 61 | void setClipArea(long x, long y, long x1, long y1); 62 | 63 | void resetMaxMove() { _maxMove = -1; _laserForceOff = false; } 64 | void setMaxMove(long length) { _moved = 0; _maxMove = length; _laserForceOff = false; } 65 | bool maxMoveReached() { return _laserForceOff; } 66 | void getMaxMoveFinalPosition(long &x, long &y) { x = _maxMoveX; y = _maxMoveY; } 67 | 68 | void setEnable3D(bool flag) { _enable3D = flag; } 69 | void setMatrix(const Matrix3& matrix) { _matrix = matrix; } 70 | void setZDist(long dist) { _zDist = dist; } 71 | 72 | private: 73 | //! send X/Y to DAC 74 | void sendToDAC(int x, int y); 75 | 76 | //! computes the out code for line clipping 77 | int computeOutCode(long x, long y); 78 | //! returns if the line should be drawn, clips line to clip area 79 | bool clipLine(long& x0, long& y0, long& x1, long& y1); 80 | 81 | int _laserPin; 82 | 83 | FIXPT _quality; 84 | 85 | long _x; 86 | long _y; 87 | int _state; 88 | 89 | FIXPT _scale; 90 | long _offsetX; 91 | long _offsetY; 92 | 93 | long _moved; 94 | long _maxMove; 95 | bool _laserForceOff; 96 | long _maxMoveX; 97 | long _maxMoveY; 98 | 99 | long _oldX; 100 | long _oldY; 101 | 102 | long _clipXMin; 103 | long _clipYMin; 104 | long _clipXMax; 105 | long _clipYMax; 106 | 107 | bool _enable3D; 108 | Matrix3 _matrix; 109 | long _zDist; 110 | }; 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /LaserShow/LaserShow.ino: -------------------------------------------------------------------------------- 1 | // See LICENSE file for details 2 | // Copyright 2016 Florian Link (at) gmx.de 3 | 4 | #include "Laser.h" 5 | #include "Drawing.h" 6 | #include "Cube.h" 7 | #include "Objects.h" 8 | #include "Logo.h" 9 | 10 | // Create laser instance (with laser pointer connected to digital pin 5) 11 | Laser laser(5); 12 | 13 | void setup() 14 | { 15 | laser.init(); 16 | } 17 | 18 | 19 | void letterEffect() 20 | { 21 | String dyn = "AZAZAYBY"; 22 | String lu = "DELTAFLO"; 23 | int w = Drawing::stringAdvance(lu); 24 | laser.setScale(3048./w); 25 | laser.setOffset(2048,2048); 26 | for (int i = 0;i<35;i++) { 27 | Drawing::drawString(dyn, -w/2,0,4); 28 | for (int i = 0;i<8;i++){ 29 | if (lu[i]>dyn[i]) dyn[i]++; 30 | if (lu[i]'0';j--) { 142 | float scale = 0.0; 143 | float step = 0.01; 144 | for (int i = 0;i<40;i++) { 145 | laser.setScale(1); 146 | circle(); 147 | laser.setScale(scale); 148 | Drawing::drawLetter(j, -center/3, -center*2/3 + 100); 149 | scale += step; 150 | step += 0.002; 151 | } 152 | } 153 | } 154 | 155 | 156 | 157 | // simple fixed logo 158 | void drawLogoSimple() 159 | { 160 | long centerX, centerY, w,h; 161 | Drawing::calcObjectBox(draw_logo, sizeof(draw_logo)/4, centerX, centerY, w, h); 162 | laser.setScale(4096/(float)h); 163 | laser.setOffset(2048,2048); 164 | for (int i = 0;i<100;i++) { 165 | Drawing::drawObject(draw_logo, sizeof(draw_logo)/4, -centerX, -centerY); 166 | } 167 | } 168 | 169 | // logo with drawing effect 170 | void drawLogo() 171 | { 172 | long centerX, centerY, w,h; 173 | Drawing::calcObjectBox(draw_logo, sizeof(draw_logo)/4, centerX, centerY, w, h); 174 | 175 | int count = 200; 176 | laser.setScale(4096/(float)h); 177 | laser.setOffset(2048,2048); 178 | 179 | long maxMove = 0; 180 | for (int i = 0;i100) { 305 | laser.resetMaxMove(); 306 | laser.setScale(2 + SIN((i*10)%360) / 10000.); 307 | Drawing::drawObject(draw_heart, sizeof(draw_heart)/4, -centerX, -centerY); 308 | } 309 | if (i>150) { 310 | maxMove -= 400; 311 | } 312 | } 313 | laser.resetMaxMove(); 314 | } 315 | 316 | // currently unused, some more objects 317 | void drawObjects() 318 | { 319 | int count = 100; 320 | laser.setScale(2); 321 | laser.setOffset(0,0); 322 | 323 | for (int i = 0;i 4096 / scale) { 354 | break; 355 | } 356 | } 357 | if (!somethingDrawn) { scrollX = currentScroll; } 358 | scrollX += speed / scale; 359 | long elapsed = millis() - time; 360 | if (elapsed < 50) { delay(50-elapsed); } 361 | 362 | } 363 | scrollX -= currentScroll; 364 | for (int k = 0;k 450) return LUT(0); 20 | if (angle > 360 && angle < 451) return -LUT(angle-360); 21 | if (angle > 270 && angle < 361) return -LUT(360-angle); 22 | if (angle > 180 && angle < 271) return LUT(angle-180); 23 | return LUT(180-angle); 24 | } 25 | 26 | long COS(unsigned int angle) { 27 | if (angle > 360) return LUT(0); 28 | if (angle > 270 && angle < 361) return LUT(360-angle); 29 | if (angle > 180 && angle < 271) return -LUT(angle-180); 30 | if (angle > 90 && angle < 181) return -LUT(180-angle); 31 | return LUT(angle); 32 | } 33 | 34 | // fixed point multiplication 35 | static long pMultiply(long x, long y) { 36 | return ( (x * y) + PROUNDBIT) >> PSHIFT; 37 | } 38 | 39 | // ---------------------------------------------- 40 | // Matrix operation 41 | // ---------------------------------------------- 42 | Matrix3 Matrix3::multiply(const Matrix3 &mat1, const Matrix3 &mat2) { 43 | Matrix3 mat; 44 | unsigned char r,c; 45 | for (c=0; c<3; c++) 46 | for (r=0; r<3; r++) 47 | mat.m[c][r] = pMultiply(mat1.m[0][r], mat2.m[c][0]) + 48 | pMultiply(mat1.m[1][r], mat2.m[c][1]) + 49 | pMultiply(mat1.m[2][r], mat2.m[c][2]); 50 | return mat; 51 | } 52 | 53 | Matrix3 Matrix3::rotateX(const unsigned int angle) { 54 | Matrix3 mat; 55 | mat.m[1][1] = COS(angle); 56 | mat.m[1][2] = SIN(angle); 57 | mat.m[2][1] = -SIN(angle); 58 | mat.m[2][2] = COS(angle); 59 | return mat; 60 | } 61 | 62 | Matrix3 Matrix3::rotateY(const unsigned int angle) { 63 | Matrix3 mat; 64 | mat.m[0][0] = COS(angle); 65 | mat.m[0][2] = -SIN(angle); 66 | mat.m[2][0] = SIN(angle); 67 | mat.m[2][2] = COS(angle); 68 | return mat; 69 | } 70 | 71 | Matrix3 Matrix3::rotateZ(const unsigned int angle) { 72 | Matrix3 mat; 73 | mat.m[0][0] = COS(angle); 74 | mat.m[0][1] = SIN(angle); 75 | mat.m[1][0] = -SIN(angle); 76 | mat.m[1][1] = COS(angle); 77 | return mat; 78 | } 79 | 80 | void Matrix3::applyMatrix(const Matrix3& matrix, const Vector3i& in, Vector3i& out) 81 | { 82 | out.x = (matrix.m[0][0] * in.x + 83 | matrix.m[1][0] * in.y + 84 | matrix.m[2][0] * in.z) >> PSHIFT; 85 | 86 | out.y = (matrix.m[0][1] * in.x + 87 | matrix.m[1][1] * in.y + 88 | matrix.m[2][1] * in.z) >> PSHIFT; 89 | 90 | out.z = (matrix.m[0][2] * in.x + 91 | matrix.m[1][2] * in.y + 92 | matrix.m[2][2] * in.z) >> PSHIFT; 93 | 94 | } 95 | 96 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/Basics.h: -------------------------------------------------------------------------------- 1 | #ifndef BASICS_H 2 | #define BASICS_H 3 | 4 | // typedef for fix point numbers 5 | typedef long FIXPT; 6 | #define PRES 16384 7 | #define PSHIFT 14 8 | #define PROUNDBIT (1 << (PSHIFT-1)) 9 | #define FROM_FLOAT(a) (long(a*PRES)) 10 | #define FROM_INT(a) (a << PSHIFT) 11 | #define TO_INT(a) ((a + PROUNDBIT)>> PSHIFT) 12 | 13 | typedef struct { 14 | int x, y, z; 15 | } Vector3i; 16 | 17 | // fixed point identity matrix 18 | struct Matrix3 { 19 | long m[3][3] = { 20 | {PRES, 0, 0}, 21 | { 0, PRES, 0}, 22 | { 0, 0, PRES} 23 | }; 24 | static void applyMatrix(const Matrix3& matrix, const Vector3i& in, Vector3i& out); 25 | static Matrix3 multiply(const Matrix3 &mat1, const Matrix3 &mat2); 26 | static Matrix3 rotateX(const unsigned int angle); 27 | static Matrix3 rotateY(const unsigned int angle); 28 | static Matrix3 rotateZ(const unsigned int angle); 29 | }; 30 | 31 | 32 | long SIN(unsigned int angle); 33 | long COS(unsigned int angle); 34 | 35 | #endif 36 | 37 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/DAC_MCP4X.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Microchip MCP4901 / MCP4911 / MCP4921 / MCP4902 / MCP4912 / MCP4922 8/10/12-bit DAC driver 3 | * 4 | * See header file. 5 | */ 6 | 7 | #include 8 | #include "DAC_MCP4X.h" 9 | 10 | 11 | byte MCP4X::init(byte model, unsigned int vrefA, unsigned int vrefB, 12 | int ss_pin, int ldac_pin, boolean autoLatch) { 13 | 14 | const byte resolutions[] = {8,10,12}; 15 | 16 | bitwidth = resolutions[model & 2]; 17 | dual = model & MCP4X_DUAL; 18 | 19 | if (model & MCP4X_INTREF) 20 | vrefA = vrefB = 2048; 21 | this->vrefs[0] = vrefA; 22 | this->vrefs[1] = vrefB; 23 | 24 | this->ss_pin = ss_pin; 25 | this->LDAC_pin = ldac_pin; 26 | this->autoLatch = autoLatch; 27 | 28 | regs[0] = MCP4X_DEFAULTS; 29 | regs[1] = MCP4X_WRITE_B | MCP4X_DEFAULTS; 30 | 31 | return 1; 32 | } 33 | 34 | void MCP4X::begin(boolean beginSPI) { 35 | 36 | pinMode(ss_pin, OUTPUT); // Ensure that SS is set to SPI master mode 37 | pinMode(LDAC_pin, OUTPUT); 38 | 39 | digitalWrite(ss_pin, HIGH); // Unselect the device 40 | digitalWrite(LDAC_pin, HIGH); // Un-latch the output 41 | 42 | if (beginSPI) 43 | SPI.begin(); 44 | configureSPI(); 45 | 46 | 47 | } 48 | 49 | void MCP4X::configureSPI() { 50 | SPI.setBitOrder(MSBFIRST); 51 | SPI.setDataMode(SPI_MODE0); 52 | SPI.setClockDivider(SPI_CLOCK_DIV2); 53 | } 54 | 55 | // Sets the gain. These DACs support 1x and 2x gain. 56 | // vout = x/2^n * gain * VREF, where x = the argument to out(), n = number of DAC bits 57 | // Example: with 1x gain, set(100) on a 8-bit (256-step) DAC would give 58 | // an output voltage of 100/256 * VREF, while a gain of 2x would give 59 | // vout = 100/256 * VREF * 2 60 | void MCP4X::setGain2x(byte chan, boolean gain2x) { 61 | chan &= 0x1; 62 | if (gain2x) 63 | regs[chan] &= ~MCP4X_GAIN_1X; 64 | else 65 | regs[chan] |= MCP4X_GAIN_1X; 66 | } 67 | 68 | // Shuts the DAC down. Shutdown current is about 1/50 (typical) of active mode current. 69 | // My measurements say ~160-180 µA active (unloaded vout), ~3.5 µA shutdown. 70 | // Time to settle on an output value increases from ~4.5 µs to ~10 µs, though (according to the datasheet). 71 | void MCP4X::shutdown(byte chan, boolean off) { 72 | 73 | chan &= 0x1; 74 | 75 | if (off) 76 | regs[chan] &= ~MCP4X_ACTIVE; 77 | else 78 | regs[chan] |= MCP4X_ACTIVE; 79 | 80 | write(regs[chan]); 81 | } 82 | 83 | void MCP4X::setVoltage(byte chan, float v) { 84 | float data; 85 | 86 | chan &= 0x1; 87 | 88 | data = (v * 1000 * (1 << bitwidth)) / ((unsigned long) vrefs[chan] * getGain(chan)); 89 | 90 | // Serial.print("bitwidth = "); 91 | // Serial.println(bitwidth); 92 | // Serial.print("Vref = "); 93 | // Serial.println(vrefs[chan]); 94 | // Serial.print("gain = "); 95 | // Serial.println(getGain(chan)); 96 | // Serial.print("V = "); 97 | // Serial.print(v); 98 | // Serial.print(" V ["); 99 | // Serial.print(data); 100 | // Serial.print(" ["); 101 | // Serial.print((int)round(data)); 102 | // Serial.println("]"); 103 | 104 | output(chan, (int)round(data)); 105 | 106 | // Serial.print("=> V = "); 107 | // Serial.print(getVoltageMV(chan)); 108 | // Serial.println("mV"); 109 | } 110 | 111 | float MCP4X::getVoltageMV(byte chan) { 112 | unsigned int data; 113 | 114 | chan &= 1; 115 | 116 | data = (regs[chan] >> (12 - bitwidth)) & ((1 << bitwidth) - 1); 117 | 118 | Serial.println(data, HEX); 119 | 120 | return ((float) vrefs[chan] * data) / (1 << bitwidth) * getGain(chan); 121 | } 122 | 123 | // Called by the output* set of functions. 124 | void MCP4X::output2(unsigned short data_A, unsigned short data_B) { 125 | this->output(MCP4X_CHAN_A, data_A); 126 | this->output(MCP4X_CHAN_B, data_B); 127 | 128 | // Update the output, if desired. 129 | // The reason this is only in the dual-output version is simple: it's mostly useless 130 | // for the single-output version, as it would make more sense to tie the \LDAC pin 131 | // to ground, or do it manually. However, there should be a single-call method 132 | // to update *both* channels in sync, which wouldn't be possible with multiple 133 | // separate DACs (for which there is latch()). 134 | if (autoLatch) { 135 | this->latch(); 136 | } 137 | } 138 | 139 | void MCP4X::output(byte chan, unsigned short data) { 140 | // unsigned int out; 141 | 142 | const unsigned short maxval = (1 << bitwidth) - 1; 143 | 144 | chan &= 0x1; 145 | 146 | 147 | if (data > maxval) 148 | data = maxval; 149 | // data = constrain(data, 0, (1 << bitwidth) - 1); 150 | 151 | // Truncate the unused bits to fit the 8/10/12 bits the DAC accepts 152 | // if (this->bitwidth == 12) 153 | // data &= 0xfff; 154 | // else if (this->bitwidth == 10) 155 | // data &= 0x3ff; 156 | // else if (this->bitwidth == 8) 157 | // data &= 0xff; 158 | // data &= (1 << bitwidth) - 1; 159 | 160 | // clear value bits 161 | regs[chan] &= 0xF000; 162 | 163 | regs[chan] |= data << (12 - bitwidth); 164 | 165 | write(regs[chan]); 166 | // // bit 15: 0 for DAC A, 1 for DAC B. (Always 0 for MCP49x1.) 167 | // // bit 14: buffer VREF? 168 | // // bit 13: gain bit; 0 for 1x gain, 1 for 2x (thus we NOT the variable) 169 | // // bit 12: shutdown bit. 1 for active operation 170 | // // bits 11 through 0: data 171 | // uint16_t out = (chan << 15) | (this->bufferVref << 14) 172 | // | ((!this->gain2x) << 13) | (1 << 12) 173 | // | (data << (12 - this->bitwidth)); 174 | } 175 | 176 | void MCP4X::write(unsigned int data) { 177 | // Drive chip select low 178 | if (MCP4X_PORT_WRITE) 179 | PORTB &= 0xfb; // Clear PORTB pin 2 = arduino pin 10 180 | else 181 | digitalWrite(ss_pin, LOW); 182 | 183 | // Send the command and data bits 184 | SPI.transfer((data & 0xff00) >> 8); 185 | SPI.transfer(data & 0xff); 186 | 187 | // Return chip select to high 188 | if (MCP4X_PORT_WRITE) 189 | PORTB |= (1 << 2); // set PORTB pin 2 = arduino pin 10 190 | else 191 | digitalWrite(ss_pin, HIGH); 192 | } 193 | 194 | 195 | // MCP49x2 (dual DAC) only. 196 | // Send a set of new values for the DAC to output in a single function call 197 | // These DACs have a function where you can change multiple DACs at the same 198 | // time: you call output() "sequentially", one DAC at a time, and *then*, 199 | // when they've all received the output data, pull the LDAC pin low on 200 | // all DACs at once. This function pulls the LDAC pin low for long enough 201 | // for the DAC(s) to change the output. 202 | // If this function is undesired, you can simply tie the LDAC pin to ground. 203 | // When tied to ground, you need *NOT* call this function! 204 | void MCP4X::latch(void) { 205 | // The datasheet says CS must return high for 40+ ns before this function 206 | // is called: no problems, that'd be taken care of automatically, as one 207 | // clock cycle at 16 MHz is longer... and there'll be a delay of multiple. 208 | 209 | if (LDAC_pin < 0) 210 | return; 211 | 212 | // We then need to hold LDAC low for at least 100 ns, i.e ~2 clock cycles. 213 | 214 | if (MCP4X_PORT_WRITE) { 215 | // This gives ~180 ns (three clock cycles, most of which is spent low) of 216 | // low time on a Uno R3 (16 MHz), measured on a scope to make sure 217 | PORTD &= ~(1 << 7); // Uno: digital pin 7; Mega: digital pin 38 218 | asm volatile("nop"); 219 | PORTD |= (1 << 7); 220 | } else { 221 | // This takes far, FAR longer than the above despite no NOP; digitalWrite 222 | // is SLOW! For comparison: the above takes 180 ns, this takes... 3.8 us, 223 | // or 3800 ns, 21 times as long - WITHOUT having a delay in there! 224 | digitalWrite(LDAC_pin, LOW); 225 | digitalWrite(LDAC_pin, HIGH); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/DAC_MCP4X.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Thomas Backman, 2012-07 (made a proper library 2011-07-30, 3 weeks after initial) 3 | * serenity@exscape.org 4 | * Support for MCP49x2 (MCP4902, MCP4912, MCP4922) added 2012-08-30, 5 | * with a patch and testing from Jonas Gruska 6 | 7 | * Code license: BSD/MIT. Whatever; I *prefer* to be credited, 8 | * and you're *not* allowed to claim you wrote this from scratch. 9 | * Apart from that, do whatever you feel like. Commercial projects, 10 | * code modifications, etc. 11 | 12 | * Gaftech 2012-11 13 | * Some customizations to fit my needs 14 | 15 | * Pins used: 16 | * Arduino pin 11 (for Uno; for Mega: 51) to device SDI (pin 4) - fixed pin 17 | * Arduino pin 13 (for Uno; for Mega: 52) to device SCK (pin 3) - fixed pin 18 | * Any digital pin to device LDAC (DAC pin 5) (except with PortWrite, see README) 19 | * Any digital pin to device CS (DAC pin 2) (as above) 20 | * 21 | * Other DAC wirings: 22 | * Pin 1: VDD, to +5 V 23 | * Pin 5: LDAC, either to an Arduino pin, or ground to update vout automatically 24 | * Pin 6: VREF, to +5 V (or some other reference voltage 0 < VREF <= VDD) 25 | * Pin 7: VSS, to ground 26 | * Pin 8: vout 27 | * (Pin 9, for the DFN package only: VSS) 28 | 29 | * Only tested on MCP4901 (8-bit) and MCP4922 (dual 12-bit), but it should work on the others as well. 30 | * Tested on an Arduino Uno R3. 31 | */ 32 | 33 | #ifndef _DAC_MCP4X_H 34 | #define _DAC_MCP4X_H 35 | 36 | #include 37 | #include 38 | 39 | // 40 | // Params 41 | // 42 | #define MCP4X_PORT_WRITE 1 43 | #define MCP4X_NO_LDAC -1 44 | #ifndef MCP4X_PORT_WRITE 45 | #define MCP4X_PORT_WRITE 0 46 | #endif 47 | #ifndef MCP4X_DEFAULTS 48 | #define MCP4X_DEFAULTS (MCP4X_BUFFERED | MCP4X_GAIN_1X | MCP4X_ACTIVE) 49 | #endif 50 | 51 | // 52 | // Constants 53 | // 54 | 55 | // Bits 56 | #define MCP4X_WRITE_B (1 << 15) 57 | #define MCP4X_BUFFERED (1 << 14) 58 | #define MCP4X_GAIN_1X (1 << 13) 59 | #define MCP4X_ACTIVE (1 << 12) 60 | 61 | // Channels 62 | #define MCP4X_CHAN_A 0 63 | #define MCP4X_CHAN_B 1 64 | 65 | // Model identifiers 66 | #define MCP4X_8_BITS 0 67 | #define MCP4X_10_BITS 1 68 | #define MCP4X_12_BITS 2 69 | #define MCP4X_INTREF (1 << 2) 70 | #define MCP4X_DUAL (1 << 3) 71 | 72 | // Model list (each model coded on 4 bits) 73 | #define MCP4X_4801 (MCP4X_8_BITS | MCP4X_INTREF) 74 | #define MCP4X_4811 (MCP4X_10_BITS | MCP4X_INTREF) 75 | #define MCP4X_4821 (MCP4X_12_BITS | MCP4X_INTREF) 76 | #define MCP4X_4802 (MCP4X_8_BITS | MCP4X_INTREF | MCP4X_DUAL) 77 | #define MCP4X_4812 (MCP4X_10_BITS | MCP4X_INTREF | MCP4X_DUAL) 78 | #define MCP4X_4822 (MCP4X_12_BITS | MCP4X_INTREF | MCP4X_DUAL) 79 | #define MCP4X_4901 (MCP4X_8_BITS) 80 | #define MCP4X_4911 (MCP4X_10_BITS) 81 | #define MCP4X_4921 (MCP4X_12_BITS) 82 | #define MCP4X_4902 (MCP4X_8_BITS | MCP4X_DUAL) 83 | #define MCP4X_4912 (MCP4X_10_BITS | MCP4X_DUAL) 84 | #define MCP4X_4922 (MCP4X_12_BITS | MCP4X_DUAL) 85 | 86 | class MCP4X { 87 | public: 88 | 89 | byte init(byte model, 90 | unsigned int vrefA = 5000, unsigned int vrefB = 5000, 91 | int ss_pin = SS, int ldac_pin = MCP4X_NO_LDAC, boolean autoLatch = 1); 92 | void begin(boolean beginSPI = 1); 93 | 94 | void configureSPI(); 95 | 96 | void setVref(byte chan, unsigned int vref) { vrefs[chan & 1] = vref; } 97 | void setVref(unsigned int vref) { vrefs[0] = vrefs[1] = vref; } 98 | void setBuffer(byte chan, boolean buffered); 99 | void setGain2x(byte chan, boolean gain2x = 1); 100 | void setAutoLatch(boolean enabled = 1) { autoLatch = enabled; } 101 | void shutdown(byte chan, boolean off = 1); 102 | 103 | void output(byte _chan, unsigned short _out); 104 | void output(unsigned short data) { output(MCP4X_CHAN_A, data); } 105 | ; 106 | /* same as output(), but having A/B makes more sense for dual DACs */ 107 | void outputA(unsigned short data) { output(MCP4X_CHAN_A, data); } 108 | void outputB(unsigned short data) { output(MCP4X_CHAN_B, data); } 109 | void output2(unsigned short _out, unsigned short _out2); // For MCP49x2 110 | 111 | // 112 | // output-like methods that takes a voltage argument 113 | // 114 | void setVoltage(byte chan, float v); 115 | 116 | int getGain(byte chan) { return regs[chan & 1] & MCP4X_GAIN_1X ? 1 : 2; } 117 | float getVoltageMV(byte chan); 118 | 119 | 120 | /* Actually change the output, if the LDAC pin isn't shorted to ground */ 121 | void latch(void); 122 | 123 | /* Only relevant for the MCP49x2 dual DACs. 124 | * If set, calling output2() will pull the LDAC pin low automatically, 125 | * which causes the output to change. 126 | * Not required if the LDAC pin is shorted to ground, however in that case, 127 | * there will be a delay between the updating of channel A and channel B. 128 | * If sync is desired, wire the LDAC pin to the Arduino and set this to true. 129 | */ 130 | void setAutomaticallyLatchDual(bool latch) { autoLatch = latch; }; 131 | 132 | private: 133 | unsigned int vrefs[2]; 134 | unsigned int regs[2]; 135 | boolean dual; 136 | int ss_pin; 137 | int LDAC_pin; 138 | int bitwidth; 139 | boolean autoLatch; /* call latch() automatically after output2() has been called? */ 140 | 141 | void write(unsigned int data); 142 | }; 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/Drawing.cpp: -------------------------------------------------------------------------------- 1 | // See LICENSE file for details 2 | // Copyright 2016 Florian Link (at) gmx.de 3 | #include "Drawing.h" 4 | #include "Font.h" 5 | 6 | 7 | void Drawing::drawString(String text, int x, int y, int count) 8 | { 9 | for (int loop = 0; loop < count; loop ++) 10 | { 11 | int i = 0; 12 | int x1 = x; 13 | while (text.charAt(i) != '\0') 14 | { 15 | x1 += drawLetter(text.charAt(i), x1, y); 16 | i ++; 17 | } 18 | } 19 | } 20 | 21 | long Drawing::stringAdvance(String text) 22 | { 23 | long adv = 0; 24 | int i = 0; 25 | while (text.charAt(i) != '\0') 26 | { 27 | adv += advance(text.charAt(i)); 28 | i ++; 29 | } 30 | return adv; 31 | } 32 | 33 | long Drawing::advance(byte letter) 34 | { 35 | long adv = 850; 36 | if (letter == 'I') { 37 | adv = 200; 38 | } else 39 | if (letter == 'W') { 40 | adv = 1000; 41 | } 42 | return adv; 43 | } 44 | 45 | long Drawing::drawLetter(byte letter, long translateX, long translateY) 46 | { 47 | long adv = advance(letter); 48 | 49 | switch (letter) 50 | { 51 | case 'A': drawObject(draw_A, sizeof(draw_A)/4, translateX, translateY); break; 52 | case 'B': drawObject(draw_B, sizeof(draw_B)/4, translateX, translateY); break; 53 | case 'C': drawObject(draw_C, sizeof(draw_C)/4, translateX, translateY); break; 54 | case 'D': drawObject(draw_D, sizeof(draw_D)/4, translateX, translateY); break; 55 | case 'E': drawObject(draw_E, sizeof(draw_E)/4, translateX, translateY); break; 56 | case 'F': drawObject(draw_F, sizeof(draw_F)/4, translateX, translateY); break; 57 | case 'G': drawObject(draw_G, sizeof(draw_G)/4, translateX, translateY); break; 58 | case 'H': drawObject(draw_H, sizeof(draw_H)/4, translateX, translateY); break; 59 | case 'I': drawObject(draw_I, sizeof(draw_I)/4, translateX, translateY); break; 60 | case 'J': drawObject(draw_J, sizeof(draw_J)/4, translateX, translateY); break; 61 | case 'K': drawObject(draw_K, sizeof(draw_K)/4, translateX, translateY); break; 62 | case 'L': drawObject(draw_L, sizeof(draw_L)/4, translateX, translateY); break; 63 | case 'M': drawObject(draw_M, sizeof(draw_M)/4, translateX, translateY); break; 64 | case 'N': drawObject(draw_N, sizeof(draw_N)/4, translateX, translateY); break; 65 | case 'O': drawObject(draw_O, sizeof(draw_O)/4, translateX, translateY); break; 66 | case 'P': drawObject(draw_P, sizeof(draw_P)/4, translateX, translateY); break; 67 | case 'Q': drawObject(draw_Q, sizeof(draw_Q)/4, translateX, translateY); break; 68 | case 'R': drawObject(draw_R, sizeof(draw_R)/4, translateX, translateY); break; 69 | case 'S': drawObject(draw_S, sizeof(draw_S)/4, translateX, translateY); break; 70 | case 'T': drawObject(draw_T, sizeof(draw_T)/4, translateX, translateY); break; 71 | case 'U': drawObject(draw_U, sizeof(draw_U)/4, translateX, translateY); break; 72 | case 'V': drawObject(draw_V, sizeof(draw_V)/4, translateX, translateY); break; 73 | case 'W': drawObject(draw_W, sizeof(draw_W)/4, translateX, translateY); break; 74 | case 'X': drawObject(draw_X, sizeof(draw_X)/4, translateX, translateY); break; 75 | case 'Y': drawObject(draw_Y, sizeof(draw_Y)/4, translateX, translateY); break; 76 | case 'Z': drawObject(draw_Z, sizeof(draw_Z)/4, translateX, translateY); break; 77 | 78 | case '0': drawObject(draw_0, sizeof(draw_0)/4, translateX, translateY); break; 79 | case '1': drawObject(draw_1, sizeof(draw_1)/4, translateX, translateY); break; 80 | case '2': drawObject(draw_2, sizeof(draw_2)/4, translateX, translateY); break; 81 | case '3': drawObject(draw_3, sizeof(draw_3)/4, translateX, translateY); break; 82 | case '4': drawObject(draw_4, sizeof(draw_4)/4, translateX, translateY); break; 83 | case '5': drawObject(draw_5, sizeof(draw_5)/4, translateX, translateY); break; 84 | case '6': drawObject(draw_6, sizeof(draw_6)/4, translateX, translateY); break; 85 | case '7': drawObject(draw_7, sizeof(draw_7)/4, translateX, translateY); break; 86 | case '8': drawObject(draw_8, sizeof(draw_8)/4, translateX, translateY); break; 87 | case '9': drawObject(draw_9, sizeof(draw_9)/4, translateX, translateY); break; 88 | case '!': drawObject(draw_exclam, sizeof(draw_exclam)/4, translateX, translateY); break; 89 | case '?': drawObject(draw_question, sizeof(draw_question)/4, translateX, translateY); break; 90 | case '.': drawObject(draw_dot, sizeof(draw_dot)/4, translateX, translateY); break; 91 | case ' ': 92 | break; 93 | 94 | } 95 | return adv; 96 | } 97 | 98 | void Drawing::drawObject(const unsigned short* data, int size, long translateX, long translateY) 99 | { 100 | const unsigned short* d = data; 101 | unsigned short posX; 102 | unsigned short posY; 103 | while (size>0) { 104 | posX = pgm_read_word(d); 105 | d++; 106 | posY = pgm_read_word(d); 107 | d++; 108 | size--; 109 | 110 | if (posX & 0x8000) { 111 | laser.on(); 112 | } else { 113 | laser.off(); 114 | } 115 | laser.sendto((posX & 0x7fff) + translateX, posY + translateY); 116 | } 117 | laser.off(); 118 | } 119 | 120 | long SIN(unsigned int angle); 121 | long COS(unsigned int angle); 122 | 123 | void Drawing::drawObjectRotated(const unsigned short* data, int size, long centerX, long centerY, int angle) 124 | { 125 | const unsigned short* d = data; 126 | unsigned short posX; 127 | unsigned short posY; 128 | while (size>0) { 129 | posX = pgm_read_word(d); 130 | d++; 131 | posY = pgm_read_word(d); 132 | d++; 133 | size--; 134 | 135 | if (posX & 0x8000) { 136 | laser.on(); 137 | } else { 138 | laser.off(); 139 | } 140 | FIXPT x = (long)(posX & 0x7fff) - centerX; 141 | FIXPT y = ((long)posY) - centerY; 142 | FIXPT x1 = COS(angle) * x - SIN(angle) * y; 143 | FIXPT y1 = COS(angle) * y + SIN(angle) * x; 144 | laser.sendto(TO_INT(x1), TO_INT(y1)); 145 | } 146 | laser.off(); 147 | } 148 | 149 | void Drawing::drawObjectRotated3D(const unsigned short* data, int size, long centerX, long centerY, int angleX, int angleY, int zDist) 150 | { 151 | Matrix3 world; 152 | world = Matrix3::rotateX(angleX); 153 | world = Matrix3::multiply(Matrix3::rotateY(angleY), world); 154 | 155 | laser.setEnable3D(true); 156 | laser.setMatrix(world); 157 | drawObject(data,size, -centerX, -centerY); 158 | laser.setEnable3D(false); 159 | } 160 | 161 | void Drawing::calcObjectBox(const unsigned short* data, int size, long& centerX, long& centerY, long& width, long& height) 162 | { 163 | const unsigned short* d = data; 164 | unsigned short posX; 165 | unsigned short posY; 166 | unsigned short x0 = 4096; 167 | unsigned short y0 = 4096; 168 | unsigned short x1 = 0; 169 | unsigned short y1 = 0; 170 | while (size>0) { 171 | posX = pgm_read_word(d) & 0x7fff; 172 | d++; 173 | posY = pgm_read_word(d); 174 | d++; 175 | size--; 176 | if (posX < x0) x0 = posX; 177 | if (posY < y0) y0 = posY; 178 | if (posX > x1) x1 = posX; 179 | if (posY > y1) y1 = posY; 180 | } 181 | centerX = (x0 + x1) / 2; 182 | centerY = (y0 + y1) / 2; 183 | width = x1 - x0; 184 | height = y1 - y0; 185 | } 186 | 187 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/Drawing.h: -------------------------------------------------------------------------------- 1 | // See LICENSE file for details 2 | // Copyright 2016 Florian Link (at) gmx.de 3 | #ifndef DRAWING_H 4 | #define DRAWING_H 5 | 6 | #include "Laser.h" 7 | 8 | extern Laser laser; 9 | 10 | //! Allows to draw text and objects from PROGMEM. 11 | class Drawing 12 | { 13 | public: 14 | //! Draws the given string at x,y position. Count indicates how often the drawing is repeated. 15 | static void drawString(String text, int x, int y, int repeat = 1); 16 | 17 | //! Draws the given letter (A-Z, 0-9, !? are currently supported in the font), returns the x advance... 18 | static long drawLetter(byte letter, long translateX = 0, long translateY = 0); 19 | 20 | //! Get X advance for given char 21 | static long advance(byte letter); 22 | 23 | //! Get X advance for string 24 | static long stringAdvance(String text); 25 | 26 | //! Draws the given data (which needs to be in PROGMEM). Size indicates the number 27 | //! of draw commands (so it is sizeof(data)/4). 28 | static void drawObject(const unsigned short* data, int size, long translateX = 0, long translateY = 0); 29 | 30 | //! Draws the given data (which needs to be in PROGMEM). Size indicates the number 31 | //! of draw commands (so it is sizeof(data)/4). 32 | static void drawObjectRotated(const unsigned short* data, int size, long centerX, long centerY, int angle); 33 | 34 | //! Draws the object and rotates in 3D. 35 | static void drawObjectRotated3D(const unsigned short* data, int size, long centerX, long centerY, int angleX, int angleY, int fov); 36 | 37 | //! Returns the center of the object (center of bounding box) 38 | static void calcObjectBox(const unsigned short* data, int size, long& centerX, long& centerY, long& width, long& height); 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/Font.h: -------------------------------------------------------------------------------- 1 | #ifndef FONT_H 2 | #define FONT_H 3 | 4 | // Font converted from gcode generated with free stick fonts at: 5 | // http://ncplot.com/stickfont/stickfont.htm 6 | 7 | // To save memory, the move commands are stores in PROGMEM. 8 | // Each move command consists of two 16bit shorts (one for x, one for y) 9 | // The highest bit (0x8000) of x indicates if the laser should be on 10 | // and needs to be masked by the drawing code. 11 | 12 | const unsigned short draw_A[] PROGMEM = { 13 | 0x17c,0x3e8, 14 | 0x8000,0x0, 15 | 0x17c,0x3e8, 16 | 0x82f9,0x0, 17 | 0x8e,0x14d, 18 | 0x826b,0x14d, 19 | }; 20 | const unsigned short draw_B[] PROGMEM = { 21 | 0x0,0x3e8, 22 | 0x8000,0x0, 23 | 0x0,0x3e8, 24 | 0x81ac,0x3e8, 25 | 0x823b,0x3b8, 26 | 0x826b,0x388, 27 | 0x829a,0x329, 28 | 0x829a,0x2ca, 29 | 0x826b,0x26b, 30 | 0x823b,0x23b, 31 | 0x81ac,0x20b, 32 | 0x0,0x20b, 33 | 0x81ac,0x20b, 34 | 0x823b,0x1dc, 35 | 0x826b,0x1ac, 36 | 0x829a,0x14d, 37 | 0x829a,0xbe, 38 | 0x826b,0x5f, 39 | 0x823b,0x2f, 40 | 0x81ac,0x0, 41 | 0x8000,0x0, 42 | }; 43 | const unsigned short draw_C[] PROGMEM = { 44 | 0x2ca,0x2f9, 45 | 0x829a,0x359, 46 | 0x823b,0x3b8, 47 | 0x81dc,0x3e8, 48 | 0x811d,0x3e8, 49 | 0x80be,0x3b8, 50 | 0x805f,0x359, 51 | 0x802f,0x2f9, 52 | 0x8000,0x26b, 53 | 0x8000,0x17c, 54 | 0x802f,0xee, 55 | 0x805f,0x8e, 56 | 0x80be,0x2f, 57 | 0x811d,0x0, 58 | 0x81dc,0x0, 59 | 0x823b,0x2f, 60 | 0x829a,0x8e, 61 | 0x82ca,0xee, 62 | }; 63 | const unsigned short draw_D[] PROGMEM = { 64 | 0x0,0x3e8, 65 | 0x8000,0x0, 66 | 0x0,0x3e8, 67 | 0x814d,0x3e8, 68 | 0x81dc,0x3b8, 69 | 0x823b,0x359, 70 | 0x826b,0x2f9, 71 | 0x829a,0x26b, 72 | 0x829a,0x17c, 73 | 0x826b,0xee, 74 | 0x823b,0x8e, 75 | 0x81dc,0x2f, 76 | 0x814d,0x0, 77 | 0x8000,0x0, 78 | }; 79 | const unsigned short draw_E[] PROGMEM = { 80 | 0x0,0x3e8, 81 | 0x8000,0x0, 82 | 0x0,0x3e8, 83 | 0x826b,0x3e8, 84 | 0x0,0x20b, 85 | 0x817c,0x20b, 86 | 0x0,0x0, 87 | 0x826b,0x0, 88 | }; 89 | const unsigned short draw_F[] PROGMEM = { 90 | 0x0,0x3e8, 91 | 0x8000,0x0, 92 | 0x0,0x3e8, 93 | 0x826b,0x3e8, 94 | 0x0,0x20b, 95 | 0x817c,0x20b, 96 | }; 97 | const unsigned short draw_G[] PROGMEM = { 98 | 0x2ca,0x2f9, 99 | 0x829a,0x359, 100 | 0x823b,0x3b8, 101 | 0x81dc,0x3e8, 102 | 0x811d,0x3e8, 103 | 0x80be,0x3b8, 104 | 0x805f,0x359, 105 | 0x802f,0x2f9, 106 | 0x8000,0x26b, 107 | 0x8000,0x17c, 108 | 0x802f,0xee, 109 | 0x805f,0x8e, 110 | 0x80be,0x2f, 111 | 0x811d,0x0, 112 | 0x81dc,0x0, 113 | 0x823b,0x2f, 114 | 0x829a,0x8e, 115 | 0x82ca,0xee, 116 | 0x82ca,0x17c, 117 | 0x1dc,0x17c, 118 | 0x82ca,0x17c, 119 | }; 120 | const unsigned short draw_H[] PROGMEM = { 121 | 0x0,0x3e8, 122 | 0x8000,0x0, 123 | 0x29a,0x3e8, 124 | 0x829a,0x0, 125 | 0x0,0x20b, 126 | 0x829a,0x20b, 127 | }; 128 | const unsigned short draw_I[] PROGMEM = { 129 | 0x0,0x3e8, 130 | 0x8000,0x0, 131 | }; 132 | const unsigned short draw_J[] PROGMEM = { 133 | 0x1dc,0x3e8, 134 | 0x81dc,0xee, 135 | 0x81ac,0x5f, 136 | 0x817c,0x2f, 137 | 0x811d,0x0, 138 | 0x80be,0x0, 139 | 0x805f,0x2f, 140 | 0x802f,0x5f, 141 | 0x8000,0xee, 142 | 0x8000,0x14d, 143 | }; 144 | const unsigned short draw_K[] PROGMEM = { 145 | 0x0,0x3e8, 146 | 0x8000,0x0, 147 | 0x29a,0x3e8, 148 | 0x8000,0x14d, 149 | 0xee,0x23b, 150 | 0x829a,0x0, 151 | }; 152 | const unsigned short draw_L[] PROGMEM = { 153 | 0x0,0x3e8, 154 | 0x8000,0x0, 155 | 0x823b,0x0, 156 | }; 157 | const unsigned short draw_M[] PROGMEM = { 158 | 0x0,0x3e8, 159 | 0x8000,0x0, 160 | 0x0,0x3e8, 161 | 0x817c,0x0, 162 | 0x2f9,0x3e8, 163 | 0x817c,0x0, 164 | 0x2f9,0x3e8, 165 | 0x82f9,0x0, 166 | }; 167 | const unsigned short draw_N[] PROGMEM = { 168 | 0x0,0x3e8, 169 | 0x8000,0x0, 170 | 0x0,0x3e8, 171 | 0x829a,0x0, 172 | 0x29a,0x3e8, 173 | 0x829a,0x0, 174 | }; 175 | const unsigned short draw_O[] PROGMEM = { 176 | 0x11d,0x3e8, 177 | 0x80be,0x3b8, 178 | 0x805f,0x359, 179 | 0x802f,0x2f9, 180 | 0x8000,0x26b, 181 | 0x8000,0x17c, 182 | 0x802f,0xee, 183 | 0x805f,0x8e, 184 | 0x80be,0x2f, 185 | 0x811d,0x0, 186 | 0x81dc,0x0, 187 | 0x823b,0x2f, 188 | 0x829a,0x8e, 189 | 0x82ca,0xee, 190 | 0x82f9,0x17c, 191 | 0x82f9,0x26b, 192 | 0x82ca,0x2f9, 193 | 0x829a,0x359, 194 | 0x823b,0x3b8, 195 | 0x81dc,0x3e8, 196 | 0x811d,0x3e8, 197 | }; 198 | const unsigned short draw_P[] PROGMEM = { 199 | 0x0,0x3e8, 200 | 0x8000,0x0, 201 | 0x0,0x3e8, 202 | 0x81ac,0x3e8, 203 | 0x823b,0x3b8, 204 | 0x826b,0x388, 205 | 0x829a,0x329, 206 | 0x829a,0x29a, 207 | 0x826b,0x23b, 208 | 0x823b,0x20b, 209 | 0x81ac,0x1dc, 210 | 0x8000,0x1dc, 211 | }; 212 | const unsigned short draw_Q[] PROGMEM = { 213 | 0x11d,0x447, 214 | 0x80be,0x417, 215 | 0x805f,0x3b8, 216 | 0x802f,0x359, 217 | 0x8000,0x2ca, 218 | 0x8000,0x1dc, 219 | 0x802f,0x14d, 220 | 0x805f,0xee, 221 | 0x80be,0x8e, 222 | 0x811d,0x5f, 223 | 0x81dc,0x5f, 224 | 0x823b,0x8e, 225 | 0x829a,0xee, 226 | 0x82ca,0x14d, 227 | 0x82f9,0x1dc, 228 | 0x82f9,0x2ca, 229 | 0x82ca,0x359, 230 | 0x829a,0x3b8, 231 | 0x823b,0x417, 232 | 0x81dc,0x447, 233 | 0x811d,0x447, 234 | 0x1ac,0x11d, 235 | 0x82ca,0x0, 236 | }; 237 | const unsigned short draw_R[] PROGMEM = { 238 | 0x0,0x3e8, 239 | 0x8000,0x0, 240 | 0x0,0x3e8, 241 | 0x81ac,0x3e8, 242 | 0x823b,0x3b8, 243 | 0x826b,0x388, 244 | 0x829a,0x329, 245 | 0x829a,0x2ca, 246 | 0x826b,0x26b, 247 | 0x823b,0x23b, 248 | 0x81ac,0x20b, 249 | 0x8000,0x20b, 250 | 0x14d,0x20b, 251 | 0x829a,0x0, 252 | }; 253 | const unsigned short draw_S[] PROGMEM = { 254 | 0x29a,0x359, 255 | 0x823b,0x3b8, 256 | 0x81ac,0x3e8, 257 | 0x80ee,0x3e8, 258 | 0x805f,0x3b8, 259 | 0x8000,0x359, 260 | 0x8000,0x2f9, 261 | 0x802f,0x29a, 262 | 0x805f,0x26b, 263 | 0x80be,0x23b, 264 | 0x81dc,0x1dc, 265 | 0x823b,0x1ac, 266 | 0x826b,0x17c, 267 | 0x829a,0x11d, 268 | 0x829a,0x8e, 269 | 0x823b,0x2f, 270 | 0x81ac,0x0, 271 | 0x80ee,0x0, 272 | 0x805f,0x2f, 273 | 0x8000,0x8e, 274 | }; 275 | const unsigned short draw_T[] PROGMEM = { 276 | 0x14d,0x3e8, 277 | 0x814d,0x0, 278 | 0x0,0x3e8, 279 | 0x829a,0x3e8, 280 | }; 281 | const unsigned short draw_U[] PROGMEM = { 282 | 0x0,0x3e8, 283 | 0x8000,0x11d, 284 | 0x802f,0x8e, 285 | 0x808e,0x2f, 286 | 0x811d,0x0, 287 | 0x817c,0x0, 288 | 0x820b,0x2f, 289 | 0x826b,0x8e, 290 | 0x829a,0x11d, 291 | 0x829a,0x3e8, 292 | }; 293 | const unsigned short draw_V[] PROGMEM = { 294 | 0x0,0x3e8, 295 | 0x817c,0x0, 296 | 0x2f9,0x3e8, 297 | 0x817c,0x0, 298 | }; 299 | const unsigned short draw_W[] PROGMEM = { 300 | 0x0,0x3e8, 301 | 0x80ee,0x0, 302 | 0x1dc,0x3e8, 303 | 0x80ee,0x0, 304 | 0x1dc,0x3e8, 305 | 0x82ca,0x0, 306 | 0x3b8,0x3e8, 307 | 0x82ca,0x0, 308 | }; 309 | const unsigned short draw_X[] PROGMEM = { 310 | 0x0,0x3e8, 311 | 0x829a,0x0, 312 | 0x29a,0x3e8, 313 | 0x8000,0x0, 314 | }; 315 | const unsigned short draw_Y[] PROGMEM = { 316 | 0x0,0x3e8, 317 | 0x817c,0x20b, 318 | 0x817c,0x0, 319 | 0x2f9,0x3e8, 320 | 0x817c,0x20b, 321 | }; 322 | const unsigned short draw_Z[] PROGMEM = { 323 | 0x29a,0x3e8, 324 | 0x8000,0x0, 325 | 0x0,0x3e8, 326 | 0x829a,0x3e8, 327 | 0x0,0x0, 328 | 0x829a,0x0, 329 | }; 330 | const unsigned short draw_0[] PROGMEM = { 331 | 0x11d,0x3e8, 332 | 0x808e,0x3b8, 333 | 0x802f,0x329, 334 | 0x8000,0x23b, 335 | 0x8000,0x1ac, 336 | 0x802f,0xbe, 337 | 0x808e,0x2f, 338 | 0x811d,0x0, 339 | 0x817c,0x0, 340 | 0x820b,0x2f, 341 | 0x826b,0xbe, 342 | 0x829a,0x1ac, 343 | 0x829a,0x23b, 344 | 0x826b,0x329, 345 | 0x820b,0x3b8, 346 | 0x817c,0x3e8, 347 | 0x811d,0x3e8, 348 | }; 349 | const unsigned short draw_1[] PROGMEM = { 350 | 0x0,0x329, 351 | 0x805f,0x359, 352 | 0x80ee,0x3e8, 353 | 0x80ee,0x0, 354 | }; 355 | const unsigned short draw_2[] PROGMEM = { 356 | 0x2f,0x2f9, 357 | 0x802f,0x329, 358 | 0x805f,0x388, 359 | 0x808e,0x3b8, 360 | 0x80ee,0x3e8, 361 | 0x81ac,0x3e8, 362 | 0x820b,0x3b8, 363 | 0x823b,0x388, 364 | 0x826b,0x329, 365 | 0x826b,0x2ca, 366 | 0x823b,0x26b, 367 | 0x81dc,0x1dc, 368 | 0x8000,0x0, 369 | 0x829a,0x0, 370 | }; 371 | const unsigned short draw_3[] PROGMEM = { 372 | 0x5f,0x3e8, 373 | 0x826b,0x3e8, 374 | 0x814d,0x26b, 375 | 0x81dc,0x26b, 376 | 0x823b,0x23b, 377 | 0x826b,0x20b, 378 | 0x829a,0x17c, 379 | 0x829a,0x11d, 380 | 0x826b,0x8e, 381 | 0x820b,0x2f, 382 | 0x817c,0x0, 383 | 0x80ee,0x0, 384 | 0x805f,0x2f, 385 | 0x802f,0x5f, 386 | 0x8000,0xbe, 387 | }; 388 | const unsigned short draw_4[] PROGMEM = { 389 | 0x1dc,0x3e8, 390 | 0x8000,0x14d, 391 | 0x82ca,0x14d, 392 | 0x1dc,0x3e8, 393 | 0x81dc,0x0, 394 | }; 395 | const unsigned short draw_5[] PROGMEM = { 396 | 0x23b,0x3e8, 397 | 0x805f,0x3e8, 398 | 0x802f,0x23b, 399 | 0x805f,0x26b, 400 | 0x80ee,0x29a, 401 | 0x817c,0x29a, 402 | 0x820b,0x26b, 403 | 0x826b,0x20b, 404 | 0x829a,0x17c, 405 | 0x829a,0x11d, 406 | 0x826b,0x8e, 407 | 0x820b,0x2f, 408 | 0x817c,0x0, 409 | 0x80ee,0x0, 410 | 0x805f,0x2f, 411 | 0x802f,0x5f, 412 | 0x8000,0xbe, 413 | }; 414 | const unsigned short draw_6[] PROGMEM = { 415 | 0x23b,0x359, 416 | 0x820b,0x3b8, 417 | 0x817c,0x3e8, 418 | 0x811d,0x3e8, 419 | 0x808e,0x3b8, 420 | 0x802f,0x329, 421 | 0x8000,0x23b, 422 | 0x8000,0x14d, 423 | 0x802f,0x8e, 424 | 0x808e,0x2f, 425 | 0x811d,0x0, 426 | 0x814d,0x0, 427 | 0x81dc,0x2f, 428 | 0x823b,0x8e, 429 | 0x826b,0x11d, 430 | 0x826b,0x14d, 431 | 0x823b,0x1dc, 432 | 0x81dc,0x23b, 433 | 0x814d,0x26b, 434 | 0x811d,0x26b, 435 | 0x808e,0x23b, 436 | 0x802f,0x1dc, 437 | 0x8000,0x14d, 438 | }; 439 | const unsigned short draw_7[] PROGMEM = { 440 | 0x29a,0x3e8, 441 | 0x80be,0x0, 442 | 0x0,0x3e8, 443 | 0x829a,0x3e8, 444 | }; 445 | const unsigned short draw_8[] PROGMEM = { 446 | 0xee,0x3e8, 447 | 0x805f,0x3b8, 448 | 0x802f,0x359, 449 | 0x802f,0x2f9, 450 | 0x805f,0x29a, 451 | 0x80be,0x26b, 452 | 0x817c,0x23b, 453 | 0x820b,0x20b, 454 | 0x826b,0x1ac, 455 | 0x829a,0x14d, 456 | 0x829a,0xbe, 457 | 0x826b,0x5f, 458 | 0x823b,0x2f, 459 | 0x81ac,0x0, 460 | 0x80ee,0x0, 461 | 0x805f,0x2f, 462 | 0x802f,0x5f, 463 | 0x8000,0xbe, 464 | 0x8000,0x14d, 465 | 0x802f,0x1ac, 466 | 0x808e,0x20b, 467 | 0x811d,0x23b, 468 | 0x81dc,0x26b, 469 | 0x823b,0x29a, 470 | 0x826b,0x2f9, 471 | 0x826b,0x359, 472 | 0x823b,0x3b8, 473 | 0x81ac,0x3e8, 474 | 0x80ee,0x3e8, 475 | }; 476 | const unsigned short draw_9[] PROGMEM = { 477 | 0x26b,0x29a, 478 | 0x823b,0x20b, 479 | 0x81dc,0x1ac, 480 | 0x814d,0x17c, 481 | 0x811d,0x17c, 482 | 0x808e,0x1ac, 483 | 0x802f,0x20b, 484 | 0x8000,0x29a, 485 | 0x8000,0x2ca, 486 | 0x802f,0x359, 487 | 0x808e,0x3b8, 488 | 0x811d,0x3e8, 489 | 0x814d,0x3e8, 490 | 0x81dc,0x3b8, 491 | 0x823b,0x359, 492 | 0x826b,0x29a, 493 | 0x826b,0x1ac, 494 | 0x823b,0xbe, 495 | 0x81dc,0x2f, 496 | 0x814d,0x0, 497 | 0x80ee,0x0, 498 | 0x805f,0x2f, 499 | 0x802f,0x8e, 500 | }; 501 | const unsigned short draw_exclam[] PROGMEM = { 502 | 0x2f,0x3e8, 503 | 0x802f,0x14d, 504 | 0x2f,0x5f, 505 | 0x8000,0x2f, 506 | 0x802f,0x0, 507 | 0x805f,0x2f, 508 | 0x802f,0x5f, 509 | }; 510 | 511 | const unsigned short draw_dot[] PROGMEM = { 512 | 0x11d,0x5f, 513 | 0x80ee,0x2f, 514 | 0x811d,0x0, 515 | 0x814d,0x2f, 516 | 0x811d,0x5f, 517 | }; 518 | 519 | const unsigned short draw_question[] PROGMEM = { 520 | 0x0,0x2f9, 521 | 0x8000,0x329, 522 | 0x802f,0x388, 523 | 0x805f,0x3b8, 524 | 0x80be,0x3e8, 525 | 0x817c,0x3e8, 526 | 0x81dc,0x3b8, 527 | 0x820b,0x388, 528 | 0x823b,0x329, 529 | 0x823b,0x2ca, 530 | 0x820b,0x26b, 531 | 0x81dc,0x23b, 532 | 0x811d,0x1dc, 533 | 0x811d,0x14d, 534 | 0x11d,0x5f, 535 | 0x80ee,0x2f, 536 | 0x811d,0x0, 537 | 0x814d,0x2f, 538 | 0x811d,0x5f, 539 | }; 540 | const unsigned short draw_slash[] PROGMEM = { 541 | 0x359,0x5f3, 542 | 0x8000,0x0, 543 | }; 544 | const unsigned short draw_open[] PROGMEM = { 545 | 0x0,0x5f3, 546 | 0x8000,0x0, 547 | 0x2f,0x5f3, 548 | 0x802f,0x0, 549 | 0x0,0x5f3, 550 | 0x814d,0x5f3, 551 | 0x0,0x0, 552 | 0x814d,0x0, 553 | }; 554 | const unsigned short draw_close[] PROGMEM = { 555 | 0x11d,0x5f3, 556 | 0x811d,0x0, 557 | 0x14d,0x5f3, 558 | 0x814d,0x0, 559 | 0x0,0x5f3, 560 | 0x814d,0x5f3, 561 | 0x0,0x0, 562 | 0x814d,0x0, 563 | }; 564 | #endif 565 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/Laser.cpp: -------------------------------------------------------------------------------- 1 | // See LICENSE file for details 2 | // Copyright 2016 Florian Link (at) gmx.de 3 | #include "Laser.h" 4 | 5 | // these values can be adapted to fine tune sendto: 6 | 7 | // if this is enabled, pins need to be 10 and 7 in dac init below, but it is a big speedup! 8 | #define MCP4X_PORT_WRITE 1 9 | 10 | #include "DAC_MCP4X.h" 11 | 12 | MCP4X dac; 13 | 14 | Laser::Laser(int laserPin) 15 | { 16 | _laserPin = laserPin; 17 | _quality = FROM_FLOAT(1./(LASER_QUALITY)); 18 | 19 | _x = 0; 20 | _y = 0; 21 | _oldX = 0; 22 | _oldY = 0; 23 | 24 | _state = 0; 25 | 26 | _scale = 1; 27 | _offsetX = 0; 28 | _offsetY = 0; 29 | 30 | _moved = 0; 31 | _maxMove = -1; 32 | _laserForceOff = false; 33 | resetClipArea(); 34 | 35 | _enable3D = false; 36 | _zDist = 1000; 37 | } 38 | 39 | void Laser::init() 40 | { 41 | dac.init(MCP4X_4822, 5000, 5000, 42 | 10, 7, 1); 43 | dac.setGain2x(MCP4X_CHAN_A, 0); 44 | dac.setGain2x(MCP4X_CHAN_B, 0); 45 | dac.begin(1); 46 | 47 | pinMode(_laserPin, OUTPUT); 48 | } 49 | 50 | void Laser::sendToDAC(int x, int y) 51 | { 52 | #ifdef LASER_SWAP_XY 53 | int x1 = y; 54 | int y1 = x; 55 | #else 56 | int x1 = x; 57 | int y1 = y; 58 | #endif 59 | #ifdef LASER_FLIP_X 60 | x1 = 4095 - x1; 61 | #endif 62 | #ifdef LASER_FLIP_Y 63 | y1 = 4095 - y1; 64 | #endif 65 | dac.output2(x1, y1); 66 | } 67 | 68 | void Laser::resetClipArea() 69 | { 70 | _clipXMin = 0; 71 | _clipYMin = 0; 72 | _clipXMax = 4095; 73 | _clipYMax = 4095; 74 | } 75 | 76 | void Laser::setClipArea(long x, long y, long x1, long y1) 77 | { 78 | _clipXMin = x; 79 | _clipYMin = y; 80 | _clipXMax = x1; 81 | _clipYMax = y1; 82 | } 83 | 84 | const int INSIDE = 0; // 0000 85 | const int LEFT = 1; // 0001 86 | const int RIGHT = 2; // 0010 87 | const int BOTTOM = 4; // 0100 88 | const int TOP = 8; // 1000 89 | 90 | int Laser::computeOutCode(long x, long y) 91 | { 92 | int code = INSIDE; // initialised as being inside of [[clip window]] 93 | 94 | if (x < _clipXMin) // to the left of clip window 95 | code |= LEFT; 96 | else if (x > _clipXMax) // to the right of clip window 97 | code |= RIGHT; 98 | if (y < _clipYMin) // below the clip window 99 | code |= BOTTOM; 100 | else if (y > _clipYMax) // above the clip window 101 | code |= TOP; 102 | 103 | return code; 104 | } 105 | 106 | // Cohen–Sutherland clipping algorithm clips a line from 107 | // P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with 108 | // diagonal from (_clipXMin, _clipYMin) to (_clipXMax, _clipYMax). 109 | bool Laser::clipLine(long& x0, long& y0, long& x1, long& y1) 110 | { 111 | // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle 112 | int outcode0 = computeOutCode(x0, y0); 113 | int outcode1 = computeOutCode(x1, y1); 114 | bool accept = false; 115 | 116 | while (true) { 117 | if (!(outcode0 | outcode1)) { // Bitwise OR is 0. Trivially accept and get out of loop 118 | accept = true; 119 | break; 120 | } else if (outcode0 & outcode1) { // Bitwise AND is not 0. Trivially reject and get out of loop 121 | break; 122 | } else { 123 | // failed both tests, so calculate the line segment to clip 124 | // from an outside point to an intersection with clip edge 125 | long x, y; 126 | 127 | // At least one endpoint is outside the clip rectangle; pick it. 128 | int outcodeOut = outcode0 ? outcode0 : outcode1; 129 | 130 | // Now find the intersection point; 131 | // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0) 132 | if (outcodeOut & TOP) { // point is above the clip rectangle 133 | x = x0 + (x1 - x0) * float(_clipYMax - y0) / float(y1 - y0); 134 | y = _clipYMax; 135 | } else if (outcodeOut & BOTTOM) { // point is below the clip rectangle 136 | x = x0 + (x1 - x0) * float(_clipYMin - y0) / float(y1 - y0); 137 | y = _clipYMin; 138 | } else if (outcodeOut & RIGHT) { // point is to the right of clip rectangle 139 | y = y0 + (y1 - y0) * float(_clipXMax - x0) / float(x1 - x0); 140 | x = _clipXMax; 141 | } else if (outcodeOut & LEFT) { // point is to the left of clip rectangle 142 | y = y0 + (y1 - y0) * float(_clipXMin - x0) / float(x1 - x0); 143 | x = _clipXMin; 144 | } 145 | 146 | // Now we move outside point to intersection point to clip 147 | // and get ready for next pass. 148 | if (outcodeOut == outcode0) { 149 | x0 = x; 150 | y0 = y; 151 | outcode0 = computeOutCode(x0, y0); 152 | } else { 153 | x1 = x; 154 | y1 = y; 155 | outcode1 = computeOutCode(x1, y1); 156 | } 157 | } 158 | } 159 | return accept; 160 | } 161 | 162 | void Laser::sendto (long xpos, long ypos) 163 | { 164 | if (_enable3D) { 165 | Vector3i p1; 166 | Vector3i p; 167 | p1.x = xpos; 168 | p1.y = ypos; 169 | p1.z = 0; 170 | Matrix3::applyMatrix(_matrix, p1, p); 171 | xpos = ((_zDist*(long)p.x) / (_zDist + (long)p.z)) + 2048; 172 | ypos = ((_zDist*(long)p.y) / (_zDist + (long)p.z)) + 2048; 173 | } 174 | // Float was too slow on Arduino, so I used 175 | // fixed point precision here: 176 | long xNew = TO_INT(xpos * _scale) + _offsetX; 177 | long yNew = TO_INT(ypos * _scale) + _offsetY; 178 | long clipX = xNew; 179 | long clipY = yNew; 180 | long oldX = _oldX; 181 | long oldY = _oldY; 182 | if (clipLine(oldX,oldY, clipX,clipY)) { 183 | if (oldX != _oldX || oldY != _oldY) { 184 | sendtoRaw(oldX, oldY); 185 | } 186 | sendtoRaw(clipX, clipY); 187 | } 188 | _oldX = xNew; 189 | _oldY = yNew; 190 | } 191 | 192 | void Laser::sendtoRaw (long xNew, long yNew) 193 | { 194 | // devide into equal parts, using _quality 195 | long fdiffx = xNew - _x; 196 | long fdiffy = yNew - _y; 197 | long diffx = TO_INT(abs(fdiffx) * _quality); 198 | long diffy = TO_INT(abs(fdiffy) * _quality); 199 | 200 | // store movement for max move 201 | long moved = _moved; 202 | _moved += abs(fdiffx) + abs(fdiffy); 203 | 204 | // use the bigger direction 205 | if (diffx < diffy) 206 | { 207 | diffx = diffy; 208 | } 209 | fdiffx = FROM_INT(fdiffx) / diffx; 210 | fdiffy = FROM_INT(fdiffy) / diffx; 211 | // interpolate in FIXPT 212 | FIXPT tmpx = 0; 213 | FIXPT tmpy = 0; 214 | for (int i = 0; i _maxMove) { 220 | off(); 221 | _laserForceOff = true; 222 | _maxMoveX = _x + TO_INT(tmpx); 223 | _maxMoveY = _y + TO_INT(tmpy); 224 | } 225 | } 226 | tmpx += fdiffx; 227 | tmpy += fdiffy; 228 | sendToDAC(_x + TO_INT(tmpx), _y + TO_INT(tmpy)); 229 | #ifdef LASER_MOVE_DELAY 230 | wait(LASER_MOVE_DELAY); 231 | #endif 232 | } 233 | 234 | // for max move, stop if required... 235 | if (!_laserForceOff && _maxMove != -1 && _moved > _maxMove) { 236 | off(); 237 | _laserForceOff = true; 238 | _maxMoveX = xNew; 239 | _maxMoveY = yNew; 240 | } 241 | 242 | _x = xNew; 243 | _y = yNew; 244 | sendToDAC(_x, _y); 245 | wait(LASER_END_DELAY); 246 | } 247 | 248 | void Laser::drawline(long x1, long y1, long x2, long y2) 249 | { 250 | if (_x != x1 or _y != y1) 251 | { 252 | off(); 253 | sendto(x1,y1); 254 | } 255 | on(); 256 | sendto(x2,y2); 257 | wait(LASER_LINE_END_DELAY); 258 | } 259 | 260 | void Laser::on() 261 | { 262 | if (!_state && !_laserForceOff) 263 | { 264 | wait(LASER_TOGGLE_DELAY); 265 | _state = 1; 266 | digitalWrite(_laserPin, HIGH); 267 | } 268 | } 269 | 270 | void Laser::off() 271 | { 272 | if (_state) 273 | { 274 | wait(LASER_TOGGLE_DELAY); 275 | _state = 0; 276 | digitalWrite(_laserPin, LOW); 277 | } 278 | } 279 | 280 | void Laser::wait(long length) 281 | { 282 | delayMicroseconds(length); 283 | } 284 | 285 | void Laser::setScale(float scale) 286 | { 287 | _scale = FROM_FLOAT(scale); 288 | } 289 | 290 | void Laser::setOffset(long offsetX, long offsetY) 291 | { 292 | _offsetX = offsetX; 293 | _offsetY = offsetY; 294 | } 295 | 296 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/Laser.h: -------------------------------------------------------------------------------- 1 | // See LICENSE file for details 2 | // Copyright 2016 Florian Link (at) gmx.de 3 | #ifndef LASER_H 4 | #define LASER_H 5 | 6 | #include "Arduino.h" 7 | #include "Basics.h" 8 | 9 | // -- The following flags can be used to fine tune the laser timing 10 | 11 | // defines the granularity of the line interpolation. 64 means that each line is split into steps of 64 pixels in the longer direction. 12 | // setting smaller values will slow down the rendering but will cause more linearity in the galvo movement, 13 | // setting bigger values will cause faster rendering, but lines will not be straight anymore. 14 | #define LASER_QUALITY 16 15 | 16 | // Defines how long the galvos wait for the on/off toggling of the laser pointer (in microseconds), this will depend on your laser pointer. 17 | #define LASER_TOGGLE_DELAY 500 18 | // Defines how long the galvos wait at the end of a line (currently only used for the 3D cube rendering, in microseconds). 19 | #define LASER_LINE_END_DELAY 200 20 | // Defines the delay the laser waits after reaching a given position (in microseconds). 21 | #define LASER_END_DELAY 5 22 | // Defines the delay after each laser movement (used when interpolating lines, in microseconds), if not defines, 0 is used 23 | #define LASER_MOVE_DELAY 5 24 | 25 | // -- The following flags can be used to rotate/flip the output without changing the DAC wiring, just uncomment the desired swap/flip 26 | // define this to swap X and Y on the DAC 27 | #define LASER_SWAP_XY 28 | // define this to flip along the x axis 29 | #define LASER_FLIP_X 30 | // define this to flip along the y axis 31 | #define LASER_FLIP_Y 32 | 33 | //! Encapsulates the laser movement and on/off state. 34 | class Laser 35 | { 36 | public: 37 | //! The laser is initialized with the laserPin, 38 | //! which selects the digital pin that turns the laser pointer on/off. 39 | Laser(int laserPin); 40 | 41 | void init(); 42 | 43 | //! send the laser to the given position, scaled and translated and line clipped. 44 | void sendto(long x, long y); 45 | //! sends the laser to the raw position (the movement is always linearly interpolated depending on the quality, 46 | //! to avoid too fast movements. 47 | void sendtoRaw(long x, long y); 48 | 49 | //! draws a line by turning the laser off, going to x1,y1, turning it on and going to x2,y2. 50 | void drawline(long x1, long y1, long x2, long y2); 51 | 52 | void wait(long length); 53 | 54 | void on(); 55 | void off(); 56 | 57 | void setScale(float scale); 58 | void setOffset(long offsetX, long offsetY); 59 | 60 | void resetClipArea(); 61 | void setClipArea(long x, long y, long x1, long y1); 62 | 63 | void resetMaxMove() { _maxMove = -1; _laserForceOff = false; } 64 | void setMaxMove(long length) { _moved = 0; _maxMove = length; _laserForceOff = false; } 65 | bool maxMoveReached() { return _laserForceOff; } 66 | void getMaxMoveFinalPosition(long &x, long &y) { x = _maxMoveX; y = _maxMoveY; } 67 | 68 | void setEnable3D(bool flag) { _enable3D = flag; } 69 | void setMatrix(const Matrix3& matrix) { _matrix = matrix; } 70 | void setZDist(long dist) { _zDist = dist; } 71 | 72 | private: 73 | //! send X/Y to DAC 74 | void sendToDAC(int x, int y); 75 | 76 | //! computes the out code for line clipping 77 | int computeOutCode(long x, long y); 78 | //! returns if the line should be drawn, clips line to clip area 79 | bool clipLine(long& x0, long& y0, long& x1, long& y1); 80 | 81 | int _laserPin; 82 | 83 | FIXPT _quality; 84 | 85 | long _x; 86 | long _y; 87 | int _state; 88 | 89 | FIXPT _scale; 90 | long _offsetX; 91 | long _offsetY; 92 | 93 | long _moved; 94 | long _maxMove; 95 | bool _laserForceOff; 96 | long _maxMoveX; 97 | long _maxMoveY; 98 | 99 | long _oldX; 100 | long _oldY; 101 | 102 | long _clipXMin; 103 | long _clipYMin; 104 | long _clipXMax; 105 | long _clipYMax; 106 | 107 | bool _enable3D; 108 | Matrix3 _matrix; 109 | long _zDist; 110 | }; 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /LaserSpectrumAnalyzer/LaserSpectrumAnalyzer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This is a Laser Spectum Analyzer, done by Florian Link (at) gmx.de 3 | 4 | Note: It works best (without sparkling dots) with my patch on the Audio library, 5 | so that the FFT is only calculated when it is being read. 6 | 7 | You can get the patched version here: 8 | https://github.com/florianlink/Audio 9 | 10 | 11 | It is based on the following: 12 | 13 | Led matrix array Spectrum Analyser 14 | 22/11/2015 Nick Metcalfe 15 | Takes a mono analog RCA audio input (nominal 1v pp) and presents a 64-band 16 | real-time colour spectrum analyser display with peak metering. 17 | 18 | Built with Teensy 3.2 controller 19 | https://www.pjrc.com/store/teensy32.html 20 | and the Teensy Audio Library 21 | http://www.pjrc.com/teensy/td_libs_Audio.html 22 | 23 | Portions of code based on: 24 | 25 | Logarithmic band calculations http://code.compartmental.net/2007/03/21/fft-averages/ 26 | 27 | Copyright (c) 2015 Nick Metcalfe valves@alphalink.com.au 28 | Copyright (c) 2016 Florian Link 29 | All right reserved. 30 | 31 | This library is free software; you can redistribute it and/or 32 | modify it under the terms of the GNU Lesser General Public 33 | License as published by the Free Software Foundation; either 34 | version 2.1 of the License, or (at your option) any later version. 35 | This library is distributed in the hope that it will be useful, 36 | but WITHOUT ANY WARRANTY; without even the implied warranty of 37 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 38 | Lesser General Public License for more details. 39 | You should have received a copy of the GNU Lesser General Public 40 | License along with this library; if not, write to the Free Software 41 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 42 | */ 43 | 44 | // Enable this if you have my patched version of the Audio library 45 | // (get patched version here: https://github.com/florianlink/Audio) 46 | //#define PATCHED_AUDIO 47 | 48 | #include 49 | #include 50 | 51 | AudioInputAnalog adc1(A1); //xy=144,119 52 | AudioAnalyzeFFT1024 fft1024_1; //xy=498,64 53 | AudioConnection patchCord1(adc1, fft1024_1); 54 | 55 | const int m_amountOfRows = 256; 56 | const int m_amountOfColumns = 90; //128; //Number of outputs per row 57 | unsigned int logGen1024[m_amountOfColumns][2] = {0}; //Linear1024 -> Log64 mapping 58 | float m_bandVal[m_amountOfColumns] = {0.0}; //band FP value 59 | const unsigned int logAmpSteps = 256; //How many steps in the log amp converter 60 | int logAmpOffset[logAmpSteps] = {0}; //Linear64 -> Log16 mapping 61 | 62 | unsigned char m_bands[m_amountOfColumns] = {0}; //band pixel height 63 | unsigned char m_peaks[m_amountOfColumns] = {0}; //peak pixel height 64 | float m_peakVal[m_amountOfColumns] = {0.0}; //peak FP value 65 | 66 | //------------------------------------------------------------------------------------------------ 67 | // Logarithmic band calculations 68 | // http://code.compartmental.net/2007/03/21/fft-averages/ 69 | 70 | //Changing the gain changes the overall height of the bars in the display 71 | //by a proportionate amount. Note that increasing the gain for low-level 72 | //signals also increases background noise to a point where it may start to 73 | //show along the bottom of the display. m_shift can hide this. 74 | const float m_gain = 340.0; //Input gain 75 | 76 | //Shifts the bars down to conceal the zero-point as well as any additional 77 | //background noise. If random data shows at the very bottom of the display 78 | //when no input is present, increase this value by steps of 0.1 to just past 79 | //the point where it disappears. This should be 0.1 in a good quiet design. 80 | const float m_shift = 1.; //Shift bars down to mask background noise 81 | 82 | //Controls how fast each spectrum bar shrinks back down. Larger numbers are faster. 83 | float m_decay = 1.0; //Speed of band decay 84 | 85 | //Enable showing the red peak trace. Turn it off for a pure spectrum display. 86 | const bool m_showPeak = true; //Show peaks 87 | 88 | //How many 20ms cycles the peak line holds on the display before resetting. As the 89 | //peak timer is restarted by peaks being nudged up, this should remain short or 90 | //the peak display tends to get stuck. 91 | const int m_peakCounter = 5; 92 | 93 | //How many pixels of spectrum need to show on the display for the peak line to appear. 94 | //This hides the peak when no input is present in order to blank and save the display. 95 | const int m_peakDisplayThreshold = 12; //Minimum number of pixels under peak for it to show 96 | 97 | //The noise gate mutes the input when the peak is below m_peakDisplayThreshold. This 98 | //can be used to conceal narrow band background noise. 99 | const bool m_noiseGate = true; 100 | 101 | const int sampleRate = 16384; 102 | const int timeSize = 1024; //FFT points 103 | const int bandShift = 2; //First two bands contain 50hz and display switching noise. Hide them.. 104 | 105 | //Shape of spread of frequency steps within the passband. Shifts the middle part left 106 | //or right for visual balance. 0.05 for a hard logarithmic response curve to 0.40 for 107 | //a much more linear curve on two displays. 108 | const float logScale = 0.14; //Scale the logarithm curve deeper or shallower 109 | 110 | int freqToIndex(int freq); 111 | 112 | //Calculate a logarithmic set of band ranges 113 | void calcBands(void) { 114 | int bandOffset; //Bring us back toward zero for larger values of logScale 115 | for (int i = 0; i < m_amountOfColumns; i++) 116 | { 117 | int lowFreq = (int)((sampleRate/2) / (float)pow(logScale + 1, m_amountOfColumns - i)) - 1; 118 | int hiFreq = (int)((sampleRate/2) / (float)pow(logScale + 1, m_amountOfColumns - 1 - i)) - 1; 119 | int lowBound = freqToIndex(lowFreq); 120 | int hiBound = freqToIndex(hiFreq); 121 | if (i == 0) bandOffset = lowBound; 122 | lowBound -= bandOffset; 123 | hiBound -= bandOffset + 1; 124 | if (lowBound < i + bandShift) lowBound = i + bandShift; 125 | if (hiBound < i + bandShift) hiBound = i + bandShift; 126 | if (lowBound > hiBound) lowBound = hiBound; 127 | if (i == m_amountOfColumns - 1) hiBound = 511; 128 | logGen1024[i][0] = lowBound; 129 | logGen1024[i][1] = hiBound; 130 | #ifdef SERIAL_DEBUG 131 | Serial.print(i); 132 | Serial.print(" - Bounds:"); 133 | Serial.print(lowBound); 134 | Serial.print(", "); 135 | Serial.println(hiBound); 136 | #endif 137 | } 138 | } 139 | 140 | //Determine the FFT sample bandwidth 141 | float getBandWidth() 142 | { 143 | return (2.0/(float)timeSize) * (sampleRate / 2.0); 144 | } 145 | 146 | //Convert a frequency to a FFT bin index 147 | int freqToIndex(int freq) 148 | { 149 | // special case: freq is lower than the bandwidth of spectrum[0] 150 | if ( freq < getBandWidth()/2 ) return 0; 151 | // special case: freq is within the bandwidth of spectrum[512] 152 | if ( freq > sampleRate/2 - getBandWidth()/2 ) return (timeSize / 2) - 1; 153 | // all other cases 154 | float fraction = (float)freq/(float) sampleRate; 155 | int i = (int)(timeSize * fraction); 156 | return i; 157 | } 158 | 159 | //Calculate a logarithmic amplitude lookup table 160 | void calcAmplitudes() { 161 | for (int i = 0; i < logAmpSteps; i++) 162 | { 163 | float db = 1.0 - ((float) i / (float)logAmpSteps); 164 | db = (1.0 - (db * db)) * (m_amountOfRows + 1); 165 | if (db < 0) logAmpOffset[i] = -1; 166 | else logAmpOffset[i] = (int)db; 167 | #ifdef SERIAL_DEBUG 168 | Serial.print(i); 169 | Serial.print(" - Amp:"); 170 | Serial.println(logAmpOffset[i]); 171 | #endif 172 | } 173 | } 174 | 175 | int maxBeatValue = 0; 176 | 177 | void updateFFT() { 178 | static int peakCount = 0; //Peak delay counter 179 | int barValue, barPeak; //current values for bar and peak 180 | float maxPeak = 0; //Sum of all peak values in frame 181 | static bool drawPeak = true; //Show peak on display 182 | //interrupts(); 183 | // some time to gather the FFT 184 | //hold(6000); 185 | if (fft1024_1.available()) 186 | { 187 | for (int band = 0; band < m_amountOfColumns; band++) { 188 | //Get FFT data 189 | float fval = fft1024_1.read(logGen1024[band][0], logGen1024[band][1]); 190 | fval = fval * m_gain - m_shift; 191 | if (fval > logAmpSteps) fval = logAmpSteps; //don't saturate the band 192 | 193 | //process bands bar value 194 | if (m_bandVal[band] > 0) m_bandVal[band] -= m_decay; //decay current band 195 | if ((drawPeak || !m_noiseGate) && fval > m_bandVal[band]) m_bandVal[band] = fval; //set to current value if it's greater 196 | barValue = (int)m_bandVal[band]; //reduce to a pixel location 197 | if (barValue > logAmpSteps - 1) barValue = logAmpSteps - 1; //apply limits 198 | if (barValue < 0) barValue = 0; 199 | barValue = logAmpOffset[barValue]; 200 | 201 | //process peak bar value 202 | if (drawPeak || !m_noiseGate) fval = m_bandVal[band] + 0.1; //examine band data transposed slightly higher 203 | else fval += 0.1; 204 | if (fval > m_peakVal[band]) { //if value is greater than stored data 205 | m_peakVal[band] = fval; //update stored data 206 | peakCount = m_peakCounter; //Start the peak display counter 207 | } 208 | barPeak = (int)m_peakVal[band]; //extract the pixel location of peak 209 | if (barPeak > logAmpSteps - 1) barPeak = logAmpSteps - 1; //apply limits 210 | if (barPeak < 0.0) barPeak = 0.0; 211 | barPeak = logAmpOffset[barPeak]; 212 | maxPeak += barPeak; //sum up all the peaks 213 | 214 | m_bands[band] = barValue; 215 | m_peaks[band] = barPeak; 216 | } 217 | 218 | //Peak counter timeout 219 | if (peakCount > 0) { //if the peak conter is active 220 | if (--peakCount == 0) { //and decrementing it deactivates it 221 | for (int band = 0; band < m_amountOfColumns; band++) { //clear the peak values 222 | m_peakVal[band] = 0; 223 | } 224 | } 225 | } 226 | maxBeatValue = 0; 227 | for (int i = m_amountOfColumns * 6 / 10;imaxBeatValue) { 230 | maxBeatValue = m_bands[i]; 231 | } 232 | } 233 | } 234 | //noInterrupts(); 235 | } 236 | 237 | void initAudio() 238 | { 239 | AudioMemory(12); 240 | calcBands(); 241 | calcAmplitudes(); 242 | #ifdef PATCHED_AUDIO 243 | fft1024_1.calculateOnRequestOnly(true); 244 | #endif 245 | } 246 | 247 | // --- End of FFT/AUDIO ---------------------------------------------------------------------------------- 248 | 249 | #include "Laser.h" 250 | #include "Drawing.h" 251 | 252 | // Create laser instance (with laser pointer connected to digital pin 5) 253 | Laser laser(5); 254 | 255 | void setup() 256 | { 257 | laser.init(); 258 | initAudio(); 259 | } 260 | 261 | void fftLoop() 262 | { 263 | int rotate = 0; 264 | int count = 0; 265 | while (1) 266 | { 267 | updateFFT(); 268 | count++; 269 | laser.setScale(1.); 270 | laser.setOffset(0,0); 271 | 272 | if (count % 2000 > 1000) 273 | { 274 | // circle analyzer 275 | m_decay = 0.15; 276 | int i = 0; 277 | float firstX; 278 | float firstY; 279 | i=20; 280 | for (int r = 0;r<=360;r+=8,i++) 281 | { 282 | float d = ((float)m_bands[i]*3 + maxBeatValue * 0.15) /320.; 283 | float x = SIN((r + rotate) % 360) * d + 2048; 284 | float y = COS((r + rotate) % 360) *d + 2048; 285 | laser.sendto(x,y); 286 | if (r == 0) { 287 | laser.on(); 288 | firstX = x; 289 | firstY = y; 290 | } 291 | } 292 | laser.sendto(firstX,firstY); 293 | rotate += 1; 294 | laser.off(); 295 | } else 296 | { 297 | // normal analyzer 298 | m_decay = 0.5; 299 | long step = 4096/m_amountOfColumns; 300 | long pos = 0; 301 | laser.sendto(0,0); 302 | laser.on(); 303 | for (int i = 0;i 500) 310 | { 311 | laser.sendto(0,0); 312 | laser.on(); 313 | pos = 0; 314 | for (int i = 0;i