|
| 1 | +/* |
| 2 | + A Controller for the Thorlabs LDC205C/LDC500 series equipment using an ATtiny85. |
| 3 | + LDC205C: https://www.thorlabs.com/thorproduct.cfm?partnumber=LDC205C |
| 4 | + LDC500: https://www.thorlabs.com/thorproduct.cfm?partnumber=LDC500 |
| 5 | + ATtiny85 source: https://github.com/SpenceKonde/ATTinyCore |
| 6 | + |
| 7 | + Main Functions: |
| 8 | + * ENABLE/DISABLE Laser -> PIN_B2 + LED |
| 9 | + * SET Laser Current -> PIN_B4 (Change to 32 MHz, RC filtered) |
| 10 | + * GET Laser1 Current -> A3 -> i? |
| 11 | + * GET AVR Internal Temperature -> t? |
| 12 | + LDC205C -> k=50mA/V |
| 13 | + |
| 14 | + Commands Cobolt Gen5 style + minor changes for the second laser and the DHT22 sensor. |
| 15 | + |
| 16 | + Power fit: P=AI+b |
| 17 | + B1 (y-intercept) = 2,158048764451327e+01 +/- 8,999878654085594e-02 |
| 18 | + A1 (slope) = 6,979088703376868e-01 +/- 1,292424325833072e-03 |
| 19 | + |
| 20 | + Undocumented commands: |
| 21 | + rtec1drv? |
| 22 | + rtec1t? |
| 23 | + ra? |
| 24 | + glm? |
| 25 | + mev? |
| 26 | + gfv? |
| 27 | + gfd? |
| 28 | + gfbd? |
| 29 | + gcn? |
| 30 | + Note if you need a faster response time, comment out the unwanted code. |
| 31 | +*/ |
| 32 | + |
| 33 | +unsigned int serialNumber = 490; // Just 405nm+attiny85. Just don't think about it. |
| 34 | +float versionNumber = 3.0; // ATtiny85 version |
| 35 | +float time; |
| 36 | + |
| 37 | +// Temperature calibration |
| 38 | +const float At=2.5; |
| 39 | +const float Bt=-120.0; |
| 40 | + |
| 41 | +// 405nm laser in the LDC205 |
| 42 | +const float A=0.69790887; |
| 43 | +const float B=21.5804876; |
| 44 | + |
| 45 | +// They both use the same modulation coefficient, but it is better to keep them separated. |
| 46 | +const float kLDC205 = 50.0; // mA/V |
| 47 | +const float iConst = 1.535; // for 1 laser driver |
| 48 | + |
| 49 | +const int laserREM = PIN_B2; |
| 50 | +const int laserMod = PIN_B4; |
| 51 | +const int laser1CTL = A3; // 405nm laser in the LDC205 |
| 52 | + |
| 53 | +boolean interlock = false; // for future interlock feature |
| 54 | + |
| 55 | +String inputString = ""; // a string to hold incoming data |
| 56 | +boolean stringComplete = false; // whether the string is complete |
| 57 | + |
| 58 | +void setup() { |
| 59 | + pinMode(laserREM, OUTPUT); |
| 60 | + pinMode(laserMod, OUTPUT); |
| 61 | + |
| 62 | + digitalWrite(laserREM, HIGH); // turn the LED on (HIGH is the voltage level) |
| 63 | + |
| 64 | + Serial.begin(115200); |
| 65 | + // reserve 200 bytes for the inputString: |
| 66 | + inputString.reserve(200); |
| 67 | + |
| 68 | + digitalWrite(laserREM, LOW); // turn the LED off by making the voltage LOW |
| 69 | +} |
| 70 | + |
| 71 | +void setCurrent(float i) { |
| 72 | + // Lock to Max. |
| 73 | + if (i > 100.00) { i = 110.00; } |
| 74 | + int iDigit = i*iConst; |
| 75 | + analogWrite(laserMod, iDigit); |
| 76 | + Serial.println("OK\r"); |
| 77 | +} |
| 78 | + |
| 79 | +float getVoltage() { |
| 80 | + int ctlOut = analogRead(laser1CTL); // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V): |
| 81 | + return ctlOut*0.005; // voltage |
| 82 | +} |
| 83 | + |
| 84 | +float getCurrent() { |
| 85 | + return getVoltage()*kLDC205; |
| 86 | +} |
| 87 | + |
| 88 | +float getPower() { |
| 89 | + float p = (getCurrent()-B)/A; |
| 90 | + if ( p > 0 ) return p; |
| 91 | + else return 0.0; |
| 92 | +} |
| 93 | + |
| 94 | +float toFloat(String s) { |
| 95 | + char carray[s.length() + 1]; //determine size of the array |
| 96 | + s.toCharArray(carray, sizeof(carray)); //put readStringinto an array |
| 97 | + float floatNumber = atof(carray); //convert the array into a float |
| 98 | + return floatNumber; |
| 99 | +} |
| 100 | + |
| 101 | +void loop() { |
| 102 | + if (stringComplete) { // get any incoming bytes: |
| 103 | + // Status of 4 LEDs |
| 104 | + if (inputString.startsWith("leds?")) { // POWER ON + LASER ON + LASER LOCK + ERROR = 1 + 2 + 4 + 8 |
| 105 | + int leds = 1+digitalRead(laserREM)*6; |
| 106 | + Serial.println(leds); |
| 107 | + } else if (inputString.startsWith("l")) { // Laser ENABLE/DISABLE/STATE |
| 108 | + if (inputString.substring(1,2) == "0") { |
| 109 | + digitalWrite(laserREM,LOW); |
| 110 | + Serial.println("OK\r"); |
| 111 | + } else if (inputString.substring(1,2) == "1") { |
| 112 | + digitalWrite(laserREM,HIGH); |
| 113 | + Serial.println("OK\r"); |
| 114 | + } else if (inputString.substring(1,2) == "?") { |
| 115 | + Serial.println(digitalRead(laserREM)); |
| 116 | + } |
| 117 | + } else if (inputString.startsWith("i?")) { // Drive Current GET |
| 118 | + Serial.println(getCurrent()); |
| 119 | + } else if (inputString.startsWith("slc")) { // Drive Current SET |
| 120 | + setCurrent(toFloat(inputString.substring(3))); |
| 121 | + } else if (inputString.startsWith("p")) { // Power GET/SET |
| 122 | + if ((inputString.substring(1,2) == "?") || (inputString.substring(1,3) == "a?")) { |
| 123 | + Serial.println(getPower()); |
| 124 | + } else { |
| 125 | + float fp = toFloat(inputString.substring(1)); |
| 126 | + float fpi; |
| 127 | + if ( fp < 1.0 ) fpi = 0; // If power set to zero, set the current to zero. |
| 128 | + else fpi = A*fp+B; |
| 129 | + if (fpi > 100.0) fpi = 100.0; |
| 130 | + setCurrent(fpi); |
| 131 | + } |
| 132 | + } else if (( inputString.startsWith("sn?") ) || (inputString.substring(3,6) == "sn?") ) { // Serial Number |
| 133 | + Serial.println(serialNumber); |
| 134 | + } else if (inputString.startsWith("ver?")) { // Version Number |
| 135 | + Serial.println(versionNumber); |
| 136 | + } else if (inputString.startsWith("hrs?")) { // Working minutes |
| 137 | + time = millis()/60000.0; |
| 138 | + Serial.println(time); |
| 139 | + } else if (inputString.startsWith("ilk?")) { // Interlock State, no real code behind |
| 140 | + (interlock) ? Serial.println("1") : Serial.println("0"); |
| 141 | + } else if (inputString.startsWith("f?")) { // Operating fault GET, no hardware implementation behind |
| 142 | + Serial.println("0"); |
| 143 | + } else if (inputString.startsWith("?")) { // Are you there? Returns Ok (undocumented Cobolt command) |
| 144 | + Serial.println("OK"); |
| 145 | + } else if (inputString.startsWith("t?")) { // Just print the internal temperature's ADC value, and a crude calibration. |
| 146 | + Serial.println(analogRead(ADC_TEMPERATURE)*At+Bt); |
| 147 | + } else if (inputString.startsWith("@cob")) { // Cobolt controller commands (some are undocumented) |
| 148 | + if (inputString.substring(4,5) == "0") { |
| 149 | + digitalWrite(laserREM,LOW); |
| 150 | + Serial.println("OK\r"); |
| 151 | + } else if (inputString.substring(4,5) == "1") { // Laser ON after interlock. |
| 152 | + digitalWrite(laserREM,HIGH); |
| 153 | + Serial.println("OK\r"); |
| 154 | + } else if (inputString.substring(4,7) == "as?") { // Get autostart enable state. |
| 155 | + Serial.println("0"); |
| 156 | + } else if (inputString.substring(4,9) == "asdr?") { // Get direct input enable state. |
| 157 | + Serial.println("0"); |
| 158 | + } else if (inputString.substring(4,9) == "asks?") { // Get key switch state. |
| 159 | + Serial.println("1"); |
| 160 | + } else if (inputString.substring(4,9) == "asky?") { // Get key switch enable state. |
| 161 | + |
| 162 | + Serial.println("1"); |
| 163 | + } else { |
| 164 | + Serial.print("Syntax Error: "); |
| 165 | + Serial.println(inputString); |
| 166 | + } |
| 167 | + } else { |
| 168 | + Serial.print("Syntax Error: "); |
| 169 | + Serial.println(inputString); |
| 170 | + } |
| 171 | + // clear the string: |
| 172 | + inputString = ""; |
| 173 | + stringComplete = false; |
| 174 | + } |
| 175 | + serialEvent(); |
| 176 | +} |
| 177 | +/* |
| 178 | + SerialEvent occurs whenever a new data comes in the |
| 179 | + hardware serial RX. This routine is run between each |
| 180 | + time loop() runs, so using delay inside loop can delay |
| 181 | + response. Multiple bytes of data may be available. |
| 182 | + */ |
| 183 | +void serialEvent() { |
| 184 | + while (Serial.available()) { |
| 185 | + // get the new byte: |
| 186 | + char inChar = (char)Serial.read(); |
| 187 | + // add it to the inputString: |
| 188 | + inputString += inChar; |
| 189 | + // if the incoming character is a carriage return (ASCII 13), |
| 190 | + // set a flag so the main loop can do something about it: |
| 191 | + if (inChar == '\r') { |
| 192 | + stringComplete = true; |
| 193 | + } |
| 194 | + } |
| 195 | +} |
0 commit comments