├── .gitignore ├── KickStat ├── Firmware │ ├── KickStat │ │ └── KickStat.ino │ ├── MiniStatButtonCell │ │ └── MiniStatButtonCell.ino │ ├── libraries │ │ ├── Arduino-MemoryFree │ │ │ ├── MemoryFree.cpp │ │ │ ├── MemoryFree.h │ │ │ ├── README.md │ │ │ ├── examples │ │ │ │ └── BareMinimum │ │ │ │ │ └── BareMinimum.ino │ │ │ ├── license │ │ │ ├── pgmStrToRAM.cpp │ │ │ └── pgmStrToRAM.h │ │ ├── LMP91000 │ │ │ ├── LMP91000.cpp │ │ │ ├── LMP91000.h │ │ │ ├── examples │ │ │ │ ├── Chronoamperometry │ │ │ │ │ └── Chronoamperometry.ino │ │ │ │ ├── CyclicVoltammetry │ │ │ │ │ └── CyclicVoltammetry.ino │ │ │ │ ├── For_Button_Cell │ │ │ │ │ ├── 2019_04_09_1723_MiniStatButtonCellRevA_CODE │ │ │ │ │ │ └── 2019_04_09_1723_MiniStatButtonCellRevA_CODE.ino │ │ │ │ │ ├── 2019_04_09_1723_MiniStatButtonCellRevA_CODE_MODDED │ │ │ │ │ │ └── 2019_04_09_1723_MiniStatButtonCellRevA_CODE_MODDED.ino │ │ │ │ │ ├── CyclicVoltammetry-Button_Cell-for-probe_density │ │ │ │ │ │ └── CyclicVoltammetry-Button_Cell-for-probe_density.ino │ │ │ │ │ ├── EIS │ │ │ │ │ │ └── EIS.ino │ │ │ │ │ └── sketch_jun05c │ │ │ │ │ │ └── sketch_jun05c.ino │ │ │ │ ├── NormalPulseVoltammetry │ │ │ │ │ └── NormalPulseVoltammetry.ino │ │ │ │ ├── SquareWaveVoltammetry-wAD5644R-for-Aptamer │ │ │ │ │ └── SquareWaveVoltammetry-wAD5644R-for-Aptamer.ino │ │ │ │ ├── SquareWaveVoltammetry-wAD5644R-for-FeCN │ │ │ │ │ └── SquareWaveVoltammetry-wAD5644R-for-FeCN.ino │ │ │ │ ├── SquareWaveVoltammetry-wAD5644R │ │ │ │ │ └── SquareWaveVoltammetry-wAD5644R.ino │ │ │ │ └── from-au9-swv-pbs-2019-02-05-0034 │ │ │ │ │ └── from-au9-swv-pbs-2019-02-05-0034.ino │ │ │ ├── keywords.txt │ │ │ └── lmp91000.pdf │ │ └── MiniStatAnalyst │ │ │ ├── MiniStatAnalyst.cpp │ │ │ ├── MiniStatAnalyst.h │ │ │ └── keywords.txt │ └── references │ │ └── README.md └── Hardware │ └── Button-Cell │ └── RevB │ ├── Assembly-Instructions │ ├── Assembly-Instructions.txt │ ├── Bootloading-SAMD21.pdf │ ├── Drivers-SparkFun-SAMD21.pdf │ ├── KickStat-LED-Cathode-Mark.jpg │ ├── PCBWay-Ordering-Parameters-01.png │ ├── PCBWay-Ordering-Parameters-02.png │ ├── PCBWay-Ordering-Parameters-03.png │ └── Setting-up-Arduino-SparkFun-SAMD21.pdf │ ├── MiniStat-Button-BOM.xlsx │ ├── MiniStat-Button-RevB-Centroid-2019-07-29-1055.zip │ ├── MiniStat-Button-RevB-Gerbers-2019-07-29-1030.zip │ ├── MiniStat-Button-brd.pdf │ ├── MiniStat-Button-sch.pdf │ ├── MiniStat-Button.brd │ ├── MiniStat-Button.sch │ └── images │ ├── IMG_2810.JPG │ └── IMG_2840.JPG └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Mac DS_Store files in all directories 2 | .DS_Store 3 | 4 | 5 | #Ignore EAGLE board and schematic auto save files (.b#x and .s#x) in all directories 6 | *.b#? 7 | *.s#? 8 | 9 | 10 | #Development file for making local changes to Arduino libraries 11 | #Ignore in all directories 12 | .development -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/Arduino-MemoryFree/MemoryFree.cpp: -------------------------------------------------------------------------------- 1 | 2 | #ifdef __arm__ 3 | // should use uinstd.h to define sbrk but Due causes a conflict 4 | extern "C" char* sbrk(int incr); 5 | #else // __ARM__ 6 | extern char *__brkval; 7 | #endif // __arm__ 8 | 9 | int freeMemory() { 10 | char top; 11 | #ifdef __arm__ 12 | return &top - reinterpret_cast(sbrk(0)); 13 | #elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151) 14 | return &top - __brkval; 15 | #else // __arm__ 16 | return __brkval ? &top - __brkval : &top - __malloc_heap_start; 17 | #endif // __arm__ 18 | } 19 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/Arduino-MemoryFree/MemoryFree.h: -------------------------------------------------------------------------------- 1 | // memoryFree header 2 | // From http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1213583720/15 3 | // ...written by user "mem". 4 | 5 | #ifndef MEMORY_FREE_H 6 | #define MEMORY_FREE_H 7 | 8 | int freeMemory(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/Arduino-MemoryFree/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Firmware/libraries/Arduino-MemoryFree/README.md -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/Arduino-MemoryFree/examples/BareMinimum/BareMinimum.ino: -------------------------------------------------------------------------------- 1 | #include ; 2 | #include ; // not needed for new way. but good to have for reference. 3 | 4 | void setup() { 5 | // put your setup code here, to run once: 6 | Serial.begin(115200); 7 | Serial.println(getPSTR("Old way to force String to Flash")); // forced to be compiled into and read 8 | Serial.println(F("New way to force String to Flash")); // forced to be compiled into and read 9 | Serial.println(F("Free RAM = ")); //F function does the same and is now a built in library, in IDE > 1.0.0 10 | Serial.println(freeMemory(), DEC); // print how much RAM is available. 11 | // print how much RAM is available. 12 | } 13 | 14 | void loop() { 15 | // put your main code here, to run repeatedly: 16 | 17 | } -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/Arduino-MemoryFree/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012 Michael P. Flaga 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/Arduino-MemoryFree/pgmStrToRAM.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | char *to_print; 6 | char *pgmStrToRAM(PROGMEM const char *theString) { 7 | free(to_print); 8 | to_print=(char *) malloc(strlen_P(theString) + 1); 9 | strcpy_P(to_print, theString); 10 | return (to_print); 11 | } -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/Arduino-MemoryFree/pgmStrToRAM.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define getPSTR(s) pgmStrToRAM(PSTR(s)) 4 | 5 | char *pgmStrToRAM(PROGMEM const char *theString); 6 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/LMP91000.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | FILENAME: LMP91000.h 3 | AUTHOR: Orlando S. Hoilett 4 | EMAIL: ohoilett@purdue.edu 5 | VERSION: 1.0.0 6 | 7 | 8 | DESCRIPTION 9 | 10 | 11 | 12 | A FEW INSTRUCTIONS 13 | * All methods are defined and coded according to the instructions given in the 14 | * LMP91000 datsheet, December 2014 Revision, from Texas Instruments. All 15 | * references to the "datasheet" refer to this specific revision. The datasheet 16 | * is referenced in the code so that the user can have further consult if he/she 17 | * needs more information. A copy of the datasheet is included in the software 18 | * download. 19 | * 20 | * All references to "the device" refer to the LMP91000 Sensor AFE System: 21 | * Configurable AFE Potentiostat for Low-Power Chemical-Sensing Applications 22 | * Impedance Analyzer from Texas Instruments. 23 | * 24 | * TIA - Transimpedance Amplifier 25 | * TIACN - Transimpedance Amplifier Control Register (0x10) 26 | * REFCN - Reference Control Register (0x11) 27 | 28 | 29 | * UPDATES 30 | * Version 0.0 31 | * 2015/09/18:1200> 32 | * Initialization of code development. 33 | * 2015/10/12:1010> 34 | * Testing methods. 35 | * 2015/10/12:1041> 36 | * Noticed that objects cannot be instantiated in the "setup()" method. 37 | * No idea why that is. 38 | 39 | * SOURCES 40 | * Some code snippets were taken from 41 | * vicatcu. "LMP91000." Authored: Oct 27, 2014. Accessed: 42 | * September 18, 2015. GitHub. 43 | * jorgenro1. "lmp91000." Authord: Jan 26, 2015. Acccessed: 44 | * September 18, 2015. GitHub. 45 | 46 | * A couple of other useful links from TI's forum 47 | * https://e2e.ti.com/support/interface/etc_interface/f/146/t/258263 48 | * https://e2e.ti.com/support/amplifiers/precision_amplifiers/f/14/t/189399 49 | * https://e2e.ti.com/support/interface/etc_interface/f/146/t/195448 50 | * https://e2e.ti.com/support/amplifiers/precision_amplifiers/f/14/t/317192 51 | 52 | 53 | * DISCLAIMER 54 | Copyright (c) 2016 Linnes Lab, Purdue University, West Lafayette, IN, USA 55 | 56 | */ 57 | 58 | 59 | 60 | #include "LMP91000.h" 61 | 62 | 63 | /************CONSTRUCTORS*****************/ 64 | 65 | //DEFAULT CONSTRUCTOR 66 | //Initializes object 67 | LMP91000::LMP91000() 68 | { 69 | } 70 | 71 | 72 | //void setMENB(uint8_t pin) 73 | //Sets the MENB I/O pin and initializes pin with "pinMode" function. 74 | void LMP91000::setMENB(uint8_t pin) 75 | { 76 | MENB = pin; 77 | pinMode(MENB, OUTPUT); 78 | } 79 | 80 | 81 | //uint8_t LMP91000::getMENB() const 82 | //Returns the I/O pin for controlling the MENB (module enable). 83 | uint8_t LMP91000::getMENB() const 84 | { 85 | return MENB; 86 | } 87 | 88 | 89 | //void write(uint8_t reg, uint8_t data) const 90 | //@param reg: register to write to 91 | //@param data: data that will be written to register 92 | // 93 | //First ensures the device is enabled for I2C commands using the "enable()" 94 | //method. Writes to the LMP91000 I2C device using the I2C protocol for Arduino 95 | //https://www.arduino.cc/en/Reference/WireWrite 96 | //Please consult page 20, Section 7.5.1 I2C Interface and 7.5.2 Write and Read 97 | //Operation in the datsheet for more information. 98 | void LMP91000::write(uint8_t reg, uint8_t data) const 99 | { 100 | enable(); 101 | Wire.beginTransmission(LMP91000_I2C_ADDRESS); 102 | Wire.write(reg); 103 | Wire.write(data); 104 | Wire.endTransmission(); 105 | } 106 | 107 | 108 | //uint8_t read(uint8_t reg) const 109 | //@param reg: register to read from 110 | // 111 | //First ensures the device is enabled for I2C commands using the "enable()" 112 | //method. Reads from the LMP91000 I2C device using the I2C protocol for Arduino. 113 | //https://www.arduino.cc/en/Reference/WireRead 114 | // 115 | //Please consult page 20, "Section 7.5.1 I2C Interface" and "7.5.2 Write and Read 116 | //Operation" in the datsheet for more information. 117 | // 118 | //The device has must be written to first before a read operation can be performed. 119 | uint8_t LMP91000::read(uint8_t reg) const 120 | { 121 | uint8_t data = 0; 122 | 123 | enable(); 124 | 125 | Wire.beginTransmission(LMP91000_I2C_ADDRESS); 126 | Wire.write(reg); 127 | Wire.endTransmission(false); 128 | 129 | Wire.requestFrom(LMP91000_I2C_ADDRESS, 0x01); 130 | while(Wire.available()){ 131 | data = Wire.read(); 132 | } 133 | 134 | return data; 135 | } 136 | 137 | 138 | //void LMP91000::enable() const 139 | //ENABLES the LMP91000 for I2C operations. Please consult page 3, "Section 5 Pin 140 | //Configurations and Functions" for more information. 141 | // 142 | //The device is active low. 143 | void LMP91000::enable() const 144 | { 145 | digitalWrite(MENB, LOW); 146 | } 147 | 148 | 149 | //void LMP91000::disable() const 150 | //DISABLES the LMP91000 for I2C operations. Please consult page 3, "Section 5 151 | //Pin Configurations and Functions" for more information. 152 | // 153 | //The device is active low. 154 | void LMP91000::disable() const 155 | { 156 | digitalWrite(MENB, HIGH); 157 | } 158 | 159 | 160 | //boolean isReady() const 161 | //@return whether or not the device is ready. 162 | // 163 | //Reads the status register (0x00) of the LMP91000 to determine whether or not 164 | //the device is ready to accept I2C commands. 165 | // 166 | //Please consult page 21, "Section 7.6.1 STATUS -- Status Register (Address 167 | //0x00)" of the datasheet for more information. 168 | // 169 | //Default state is not ready. 170 | boolean LMP91000::isReady() const 171 | { 172 | return read(LMP91000_STATUS_REG)==LMP91000_READY; 173 | } 174 | 175 | 176 | //boolean isLocked() const 177 | //@return whether or not the TIACN and REFCN is locked for writing 178 | // 179 | ////Reads the lock register (0x01) of the LMP91000 to determine whether or not 180 | //the TIACN and REFCN are "write-enabled" or "read-only." 181 | // 182 | //Please consult pages 21 and 22, "Section 7.6.2 LOCK -- Protection Register 183 | //(Address 0x01)" for more information. 184 | // 185 | //Deafult state is "read-only" mode. 186 | boolean LMP91000::isLocked() const 187 | { 188 | return bitRead(read(LMP91000_LOCK_REG),0)==LMP91000_WRITE_LOCK; 189 | } 190 | 191 | //from vicatcu 192 | //void LMP91000::lock() const 193 | // 194 | //Writes to the lock register (0x01) of the LMP9100 to set the TIACN and REFCN 195 | //registers to "read-only." 196 | // 197 | //Please consult pages 21 and 22, "Section 7.6.2 LOCK -- Protection Register 198 | //(Address 0x01)" for more information. 199 | // 200 | //Default state is "read-only" mode. 201 | void LMP91000::lock() const 202 | { 203 | write(LMP91000_LOCK_REG, LMP91000_WRITE_LOCK); 204 | } 205 | 206 | //from vicatcu 207 | //void LMP91000::unlock() const 208 | // 209 | //Writes to the lock register (0x01) of the LMP9100 to set the TIACN and REFCN 210 | //registers to "write" mode. 211 | // 212 | //Please consult pages 21 and 22, "Section 7.6.2 LOCK -- Protection Register 213 | //(Address 0x01)" for more information. 214 | // 215 | //Default state is "read-only" mode. 216 | void LMP91000::unlock() const 217 | { 218 | write(LMP91000_LOCK_REG, LMP91000_WRITE_UNLOCK); 219 | } 220 | 221 | 222 | //void LMP91000::setGain(uint8_t gain) const 223 | //@param gain: the gain to be set to 224 | // 225 | //param - value - gain resistor 226 | //0 - 000 - External resistor 227 | //1 - 001 - 2.75 kOhm 228 | //2 - 010 - 3.5 kOhm 229 | //3 - 011 - 7 kOhm 230 | //4 - 100 - 14 kOhm 231 | //5 - 101 - 35 kOhm 232 | //6 - 110 - 120 kOhm 233 | //7 - 111 - 350 kOhm 234 | // 235 | //Sets the transimpedance amplifier gain. First reads the register to ensure 236 | //that the other bits are not affected. The 3 LSBs of "gain" parameter is 237 | //written to the 2nd, 3rd, and 4th bit of the TIACN register. 238 | // 239 | //Please consult page 14 "7.3.1.1 Transimpedance Amplifier" and page 22 "Section 240 | //7.6.3 TIACN -- TIA Control Register (Address 0x10)" of the datasheet for more 241 | //information. 242 | void LMP91000::setGain(uint8_t user_gain) 243 | { 244 | gain = user_gain; 245 | 246 | unlock(); 247 | uint8_t data = read(LMP91000_TIACN_REG); 248 | data &= ~(7 << 2); //clears bits 2-4 249 | data |= (user_gain << 2); //writes to bits 2-4 250 | write(LMP91000_TIACN_REG, data); 251 | } 252 | 253 | 254 | double LMP91000::getGain() const 255 | { 256 | if (gain == 0) return gain; 257 | else return TIA_GAIN[gain]; 258 | } 259 | 260 | //void LMP91000::setRLoad(uint8_t load) const 261 | //@param load: the internal load resistor to select 262 | // 263 | //param - value - RLoad 264 | //0 - 00 - 10 Ohm 265 | //1 - 01 - 33 Ohm 266 | //2 - 10 - 50 Ohm 267 | //3 - 11 - 100 Ohm 268 | // 269 | //Sets the internal RLOAD selection resistor. First reads the register to ensure 270 | //that the other bits are not affected. The 2 LSBs of "load" parameter is 271 | //written to the 0th and 1st bit of the TIACN register. 272 | // 273 | //Please consult page 14 "7.3.1.1 Transimpedance Amplifier" and page 22 "Section 274 | //7.6.3 TIACN -- TIA Control Register (Address 0x10)" of the datasheet for more 275 | //information. 276 | void LMP91000::setRLoad(uint8_t load) const 277 | { 278 | unlock(); 279 | uint8_t data = read(LMP91000_TIACN_REG); 280 | data &= ~3; //clears 0th and 1st bits 281 | data |= load; //writes to 0th and 1st bits 282 | write(LMP91000_TIACN_REG, data); 283 | } 284 | 285 | //void LMP91000::setRefSource(uint8_t source) const 286 | //@param source: external vs. internal 287 | // 288 | //param - result 289 | //0 - internal reference 290 | //1 - external reference 291 | // 292 | //Sets the voltage reference source of the LMP91000 to an internal reference or 293 | //an external reference. 294 | // 295 | //Please consult page 22, "Section 7.6.4 REFCN -- Reference Control Register 296 | //(Address 0x11)" of the datasheet for more information. 297 | void LMP91000::setRefSource(uint8_t source) const 298 | { 299 | if (source == 0) setIntRefSource(); 300 | else setExtRefSource(); 301 | 302 | } 303 | 304 | 305 | //void LMP91000::setIntRefSource() const 306 | // 307 | //Unlocks the REFCN register for "write" mode. First reads the register to 308 | //ensure that the other bits are not affected. Writes a "0" to the 7th bit of 309 | //the REFCN register. 310 | // 311 | //Sets the voltage reference source to supply voltage (Vdd). 312 | // 313 | //Please consult page 22, "Section 7.6.4 REFCN -- Reference Control Register 314 | //(Address 0x11)" of the datasheet for more information. 315 | void LMP91000::setIntRefSource() const 316 | { 317 | unlock(); //unlocks the REFCN register for "write" mode 318 | uint8_t data = read(LMP91000_REFCN_REG); 319 | data &= ~(1 << 7); //clears the 7th bit 320 | write(LMP91000_REFCN_REG, data); 321 | } 322 | 323 | 324 | //void LMP91000::setExtRefSource() const 325 | // 326 | //Unlocks the REFCN register for "write" mode. First reads the register to 327 | //ensure that the other bits are not affected. Writes a "1" to the 7th bit of 328 | //the REFCN register. 329 | // 330 | //Sets the reference source of the LMP91000 to an external reference provided at 331 | //the Vref pin. 332 | // 333 | //Please consult page 22, "Section 7.6.4 REFCN -- Reference Control Register 334 | //(Address 0x11)" of the datasheet for more information. 335 | void LMP91000::setExtRefSource() const 336 | { 337 | unlock(); //unlocks the REFCN register for "write" mode 338 | uint8_t data = read(LMP91000_REFCN_REG); 339 | data |= (1 << 7); //writes a "1" to the 7th bit 340 | write(LMP91000_REFCN_REG, data); 341 | } 342 | 343 | 344 | //void LMP91000::setIntZ(uint8_t intZ) const 345 | //@param intZ: the internal zero selection 346 | // 347 | //param - value - result 348 | //0 - 00 - 20% 349 | //1 - 01 - 50% 350 | //2 - 10 - 67% 351 | //3 - 11 - bypassed 352 | // 353 | //Unlocks the REFCN register for "write" mode. First reads the register to 354 | //ensure that the other bits are not affected. Writes to the 5th and 6th bits 355 | //of the REFCN register. 356 | // 357 | //Sets the internal zero of the device, particularly the transimpedance 358 | //amplifier. 359 | // 360 | //Please consult page 22, "Section 7.6.4 REFCN -- Reference Control Register 361 | //(Address 0x11)" of the datasheet for more information. 362 | void LMP91000::setIntZ(uint8_t intZ) 363 | { 364 | zero = intZ; 365 | 366 | unlock(); //unlocks the REFCN register for "write" mode 367 | uint8_t data = read(LMP91000_REFCN_REG); 368 | data &= ~(3 << 5); 369 | data |= (intZ << 5); 370 | write(LMP91000_REFCN_REG, data); 371 | } 372 | 373 | double LMP91000::getIntZ() const 374 | { 375 | return TIA_ZERO[zero]; 376 | } 377 | 378 | 379 | //void LMP91000::setBiasSign(uint8_t sign) const 380 | //0 = negative 381 | //1 = positive 382 | void LMP91000::setBiasSign(uint8_t sign) const 383 | { 384 | if (sign == 0) setNegBias(); 385 | else setPosBias(); 386 | } 387 | 388 | //void LMP91000::setNegBias() const 389 | void LMP91000::setNegBias() const 390 | { 391 | unlock(); 392 | uint8_t data = read(LMP91000_REFCN_REG); 393 | data &= ~(1 << 4); //clear bit 394 | write(LMP91000_REFCN_REG, data); 395 | } 396 | 397 | //void LMP91000::setPosBias() const 398 | void LMP91000::setPosBias() const 399 | { 400 | unlock(); 401 | uint8_t data = read(LMP91000_REFCN_REG); 402 | data |= (1 << 4); 403 | write(LMP91000_REFCN_REG, data); 404 | } 405 | 406 | //void LMP91000::setBias(uint8_t bias) const 407 | void LMP91000::setBias(uint8_t bias) const 408 | { 409 | unlock(); 410 | uint8_t data = read(LMP91000_REFCN_REG); 411 | data &= ~(0x0F); //clear the first four bits so I can bit Or in the next step 412 | data |= bias; 413 | write(LMP91000_REFCN_REG, data); 414 | } 415 | 416 | 417 | //void LMP91000::setBias(uint8_t bias) const 418 | //sign 0 is negative and 1 is positive 419 | // 420 | void LMP91000::setBias(uint8_t bias, signed char sign) const 421 | { 422 | if(sign > 0) sign = 1; 423 | else sign = 0; 424 | sign = (uint8_t)sign; 425 | 426 | if(bias > 13) bias = 0; 427 | 428 | 429 | unlock(); 430 | uint8_t data = read(LMP91000_REFCN_REG); 431 | data &= ~(0x1F); //clear the first five bits so I can bit Or in the next step 432 | data |= bias; 433 | data |= ((sign << 4) | bias); 434 | write(LMP91000_REFCN_REG, data); 435 | } 436 | 437 | 438 | //void LMP91000::setFET(uint8_t selection) const 439 | void LMP91000::setFET(uint8_t selection) const 440 | { 441 | if (selection == 0) disableFET(); 442 | else enableFET(); 443 | } 444 | 445 | //void LMP91000::disableFET() const 446 | void LMP91000::disableFET() const 447 | { 448 | uint8_t data = read(LMP91000_MODECN_REG); 449 | data &= ~(1 << 7); 450 | write(LMP91000_MODECN_REG, data); 451 | } 452 | 453 | //void LMP91000::enableFET() const 454 | void LMP91000::enableFET() const 455 | { 456 | uint8_t data = read(LMP91000_MODECN_REG); 457 | data |= (1 << 7); 458 | write(LMP91000_MODECN_REG, data); 459 | } 460 | 461 | //void LMP91000::setMode(uint8_t mode) const 462 | void LMP91000::setMode(uint8_t mode) const 463 | { 464 | if (mode == 0) sleep(); 465 | else if (mode == 1) setTwoLead(); 466 | else if (mode == 2) standby(); 467 | else if (mode == 3) setThreeLead(); 468 | else if (mode == 4) measureCell(); 469 | else if (mode == 5) getTemp(); 470 | else {}; //some error 471 | } 472 | 473 | //void LMP91000::sleep const 474 | // 475 | //Sets the 3 LSBs of the Mode Control Register (0x12) to 0. 476 | // 477 | //Places the LMP91000 in deep sleep state for power conservation. The LMP91000 478 | //consumes 0.6 uA of current in deep sleep mode. 479 | // 480 | //Please see page 19 Section, 7.4 Device Functional Modes and page 23, Section 481 | //7.6.5 MODECN -- Mode Control Register (Address 0x12) of the datasheet for more 482 | //information. 483 | void LMP91000::sleep() const 484 | { 485 | uint8_t data = read(LMP91000_MODECN_REG); 486 | data &= ~(0x07); 487 | write(LMP91000_MODECN_REG, data); 488 | } 489 | 490 | 491 | //void LMP91000::setTwoLead() const 492 | //Sets the first three bits of the Mode Control Register to 001. This enables 493 | //the LMP91000 for 2-electrode potentiometric measurements. 494 | void LMP91000::setTwoLead() const 495 | { 496 | uint8_t data = read(LMP91000_MODECN_REG); 497 | data &= ~(0x07); 498 | data |= (0x01); 499 | write(LMP91000_MODECN_REG, data); 500 | } 501 | 502 | 503 | //void LMP91000::standby() const 504 | // 505 | //Sets the 3 LSBs of the Mode Control Register (0x12) to 010. 506 | // 507 | //Places the device in standby() mode which allows quick warm-up in between tests 508 | // 509 | //Please see page 19-20, Section 7.4 Device Functional Modes and page 23 Section 510 | //7.6.5 MODECN -- Mode Control Register (Address 0x12) of the datasheet for 511 | //more information. 512 | void LMP91000::standby() const 513 | { 514 | uint8_t data = read(LMP91000_MODECN_REG); 515 | data &= ~(0x07); 516 | data |= (0x02); 517 | write(LMP91000_MODECN_REG, data); 518 | } 519 | 520 | //void LMP91000::setThreeLead() const 521 | //Sets the first three bits of the Mode Control Register to 011. This enables 522 | //the LMP91000 for 3-electrode potentiometric measurements. 523 | void LMP91000::setThreeLead() const 524 | { 525 | uint8_t data = read(LMP91000_MODECN_REG); 526 | data &= ~(0x07); 527 | data |= (0x03); 528 | write(LMP91000_MODECN_REG, data); 529 | } 530 | 531 | //void LMP91000::measureCell() const 532 | // 533 | void LMP91000::measureCell() const 534 | { 535 | uint8_t data = read(LMP91000_MODECN_REG); 536 | data &= ~(0x07); //clears the first three bits 537 | data |= (0x06); 538 | write(LMP91000_MODECN_REG, data); 539 | } 540 | 541 | //void LMP91000::getTemp() const 542 | void LMP91000::getTemp() const 543 | { 544 | uint8_t data = read(LMP91000_MODECN_REG); 545 | data |= (0x07); 546 | write(LMP91000_MODECN_REG, data); 547 | } 548 | 549 | 550 | //double LMP91000::getTemp(uint8_t sensor, double adc_ref, uint8_t adc_bits) const 551 | //returns temperatue in degrees Celsius 552 | // 553 | //Measures temperature by setting bits 0, 1, and 2 of the Mode Control Register 554 | //to 1. This sets the transimpedance amplifier of the LMP91000 ON and sends 555 | //the output of the internal temperature sensor to the VOUT pin of the LMP91000. 556 | double LMP91000::getTemp(uint8_t sensor, double adc_ref, uint8_t adc_bits) const 557 | { 558 | uint8_t data = read(LMP91000_MODECN_REG); 559 | data |= (0x07); 560 | write(LMP91000_MODECN_REG, data); 561 | 562 | delay(100); 563 | 564 | return (getVoltage(sensor, adc_ref, adc_bits)-TEMP_INTERCEPT)/TEMPSLOPE; 565 | } 566 | 567 | 568 | //uint16_t MiniStat::getOutput(uint8_t sensor) const 569 | // 570 | //@param sensor: the analog in pin of the LMP91000 is connected to 571 | // 572 | //@return the voltage output of the LMP91000 in bits 573 | // 574 | //Uses analogRead() return the output of the LMP91000. 575 | uint16_t LMP91000::getOutput(uint8_t sensor) const 576 | { 577 | return analogRead(sensor); 578 | } 579 | 580 | 581 | //double MiniStat::getVoltage(uint8_t sensor, double adc_ref, uint8_t adc_bits) const 582 | //{ 583 | // return (analogRead(sensor)*adc_ref)/(pow(10,adc_bits)-1); 584 | //} 585 | // 586 | // 587 | //double MiniStat::getCurrent(uint8_t sensor, double adc_ref, uint8_t adc_bits) const 588 | //{ 589 | // return (getVoltage(sensor, adc_ref, adc_bits) - (adc_ref/TIA_ZERO[zero]))/TIA_GAIN[gain-1]; 590 | //} 591 | // 592 | // 593 | // 594 | //double MiniStat::getCurrent(uint8_t sensor, double adc_ref, uint8_t adc_bits, double extGain) const 595 | //{ 596 | // return (getVoltage(sensor, adc_ref, adc_bits) - (adc_ref/TIA_ZERO[zero]))/extGain; 597 | //} 598 | 599 | 600 | 601 | //double MiniStat::getVoltage(uint16_t adcVal, double adc_ref, uint8_t adc_bits) const 602 | // 603 | //@param adcVal: value returned by the analog-to-digital converter of 604 | // the microcontroller used to control the LMP91000 605 | // 606 | //@param adc_ref: voltage reference of the analog-to-digtal converter 607 | // of the microcontroller 608 | // 609 | //@param adc_bits: number of bits of the analog-to-digital converter 610 | // of the microcontroller 611 | // 612 | //@return the voltage output of the LMP91000 613 | // 614 | //This method calculates the voltage at the output of the LMP91000 by multiplying 615 | //by the refernece voltage of the analog-to-digital converter and dividing by 616 | //the bit resolution of the analog-to-digital converter. 617 | double LMP91000::getVoltage(uint16_t adcVal, double adc_ref, uint8_t adc_bits) const 618 | { 619 | return (adcVal*adc_ref)/(pow(2,adc_bits)-1); 620 | } 621 | 622 | 623 | //double MiniStat::getCurrent(uint16_t adcVal, double adc_ref, uint8_t adc_bits) const 624 | // 625 | //@param adcVal: value returned by the analog-to-digital converter of 626 | // the microcontroller used to control the LMP91000 627 | // 628 | //@param adc_ref: voltage reference of the analog-to-digtal converter 629 | // of the microcontroller 630 | // 631 | //@param adc_bits: number of bits of the analog-to-digital converter 632 | // of the microcontroller 633 | // 634 | //@return the current at the working electrode 635 | // 636 | //This method calculates the current at the working electrode by reading in the 637 | //voltage at the output of LMP91000 and dividing by the value of the gain resistor. 638 | double LMP91000::getCurrent(uint16_t adcVal, double adc_ref, uint8_t adc_bits) const 639 | { 640 | return (getVoltage(adcVal, adc_ref, adc_bits) - (adc_ref*TIA_ZERO[zero]))/TIA_GAIN[gain-1]; 641 | } 642 | 643 | 644 | //double MiniStat::getCurrent(uint16_t adcVal, double adc_ref, uint8_t adc_bits, 645 | // double extGain) const 646 | // 647 | //@param adcVal: value returned by the analog-to-digital converter of 648 | // the microcontroller used to control the LMP91000 649 | // 650 | //@param adc_ref: voltage reference of the analog-to-digtal converter 651 | // of the microcontroller 652 | // 653 | //@param adc_bits: number of bits of the analog-to-digital converter 654 | // of the microcontroller 655 | // 656 | //@param extGain: value of external gain resistor 657 | // 658 | //@return the current at the working electrode 659 | // 660 | //This method calculates the current at the working electrode by reading in the 661 | //voltage at the output of LMP91000 and dividing by the value of the external 662 | //gain resistor. 663 | double LMP91000::getCurrent(uint16_t adcVal, double adc_ref, uint8_t adc_bits, 664 | double extGain) const 665 | { 666 | return (getVoltage(adcVal, adc_ref, adc_bits) - (adc_ref*TIA_ZERO[zero]))/extGain; 667 | } 668 | 669 | 670 | 671 | 672 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/LMP91000.h: -------------------------------------------------------------------------------- 1 | /* 2 | FILENAME: LMP91000.h 3 | AUTHOR: Orlando S. Hoilett 4 | EMAIL: ohoilett@purdue.edu 5 | 6 | 7 | Please see .cpp file for extended descriptions, instructions, and version updates 8 | 9 | 10 | Linnes Lab code, firmware, and software is released under the MIT License 11 | (http://opensource.org/licenses/MIT). 12 | 13 | The MIT License (MIT) 14 | 15 | Copyright (c) 2016 Linnes Lab, Purdue University, West Lafayette, IN, USA 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy 18 | of this software and associated documentation files (the "Software"), to deal 19 | in the Software without restriction, including without limitation the rights to 20 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 21 | of the Software, and to permit persons to whom the Software is furnished to do 22 | so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in all 25 | copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33 | SOFTWARE. 34 | 35 | */ 36 | 37 | 38 | 39 | 40 | #ifndef LMP91000_H 41 | #define LMP91000_H 42 | 43 | #include "Arduino.h" 44 | #include "Wire.h" 45 | 46 | const double TEMP_INTERCEPT = 1555; 47 | const double TEMPSLOPE = -8; 48 | const uint8_t LMP91000_I2C_ADDRESS = 0X48; 49 | 50 | const uint8_t LMP91000_STATUS_REG = 0x00; /* Read only status register */ 51 | const uint8_t LMP91000_LOCK_REG=0x01; /* Protection Register */ 52 | const uint8_t LMP91000_TIACN_REG=0x10; /* TIA Control Register */ 53 | const uint8_t LMP91000_REFCN_REG=0x11; /* Reference Control Register*/ 54 | const uint8_t LMP91000_MODECN_REG=0x12; /* Mode Control Register */ 55 | 56 | const uint8_t LMP91000_READY=0x01; 57 | const uint8_t LMP91000_NOT_READY=0x00; 58 | 59 | const uint8_t LMP91000_TIA_GAIN_EXT=0x00; //default 60 | const uint8_t LMP91000_TIA_GAIN_2P75K=0x04; 61 | const uint8_t LMP91000_TIA_GAIN_3P5K=0x08; 62 | const uint8_t LMP91000_TIA_GAIN_7K=0x0C; 63 | const uint8_t LMP91000_TIA_GAIN_14K=0x10; 64 | const uint8_t LMP91000_TIA_GAIN_35K=0x14; 65 | const uint8_t LMP91000_TIA_GAIN_120K=0x18; 66 | const uint8_t LMP91000_TIA_GAIN_350K=0x1C; 67 | 68 | const uint8_t LMP91000_RLOAD_10OHM=0X00; 69 | const uint8_t LMP91000_RLOAD_33OHM=0X01; 70 | const uint8_t LMP91000_RLOAD_50OHM=0X02; 71 | const uint8_t LMP91000_RLOAD_100OHM=0X03; //default 72 | 73 | const uint8_t LMP91000_REF_SOURCE_INT=0x00; //default 74 | const uint8_t LMP91000_REF_SOURCE_EXT=0x80; 75 | 76 | const uint8_t LMP91000_INT_Z_20PCT=0x00; 77 | const uint8_t LMP91000_INT_Z_50PCT=0x20; //default 78 | const uint8_t LMP91000_INT_Z_67PCT=0x40; 79 | const uint8_t LMP91000_INT_Z_BYPASS=0x60; 80 | 81 | const uint8_t LMP91000_BIAS_SIGN_NEG=0x00; //default 82 | const uint8_t LMP91000_BIAS_SIGN_POS=0x10; 83 | 84 | const uint8_t LMP91000_BIAS_0PCT=0x00; //default 85 | const uint8_t LMP91000_BIAS_1PCT=0x01; 86 | const uint8_t LMP91000_BIAS_2PCT=0x02; 87 | const uint8_t LMP91000_BIAS_4PCT=0x03; 88 | const uint8_t LMP91000_BIAS_6PCT=0x04; 89 | const uint8_t LMP91000_BIAS_8PCT=0x05; 90 | const uint8_t LMP91000_BIAS_10PCT=0x06; 91 | const uint8_t LMP91000_BIAS_12PCT=0x07; 92 | const uint8_t LMP91000_BIAS_14PCT=0x08; 93 | const uint8_t LMP91000_BIAS_16PCT=0x09; 94 | const uint8_t LMP91000_BIAS_18PCT=0x0A; 95 | const uint8_t LMP91000_BIAS_20PCT=0x0B; 96 | const uint8_t LMP91000_BIAS_22PCT=0x0C; 97 | const uint8_t LMP91000_BIAS_24PCT=0x0D; 98 | 99 | const uint8_t LMP91000_FET_SHORT_DISABLED=0x00; //default 100 | const uint8_t LMP91000_FET_SHORT_ENABLED=0x80; 101 | const uint8_t LMP91000_OP_MODE_DEEP_SLEEP=0x00; //default 102 | const uint8_t LMP91000_OP_MODE_GALVANIC=0x01; 103 | const uint8_t LMP91000_OP_MODE_STANDBY=0x02; 104 | const uint8_t LMP91000_OP_MODE_AMPEROMETRIC=0x03; 105 | const uint8_t LMP91000_OP_MODE_TIA_OFF=0x06; 106 | const uint8_t LMP91000_OP_MODE_TIA_ON=0x07; 107 | 108 | const uint8_t LMP91000_WRITE_LOCK=0x01; //default 109 | const uint8_t LMP91000_WRITE_UNLOCK=0x00; 110 | 111 | const uint8_t LMP91000_NOT_PRESENT=0xA8; // arbitrary library status code 112 | 113 | const double TIA_GAIN[] = {2750,3500,7000,14000,35000,120000,350000}; 114 | const double TIA_BIAS[] = {0, 0.01, 0.02, 0.04, 0.06, 0.08, 0.1, 0.12, 0.14, 115 | 0.16, 0.18, 0.2, 0.22, 0.24}; 116 | const uint8_t NUM_TIA_BIAS = 14; 117 | const double TIA_ZERO[] = {0.2, 0.5, 0.67}; 118 | 119 | 120 | class LMP91000 { 121 | 122 | private: 123 | 124 | uint8_t MENB; //IO pin for enabling and disabling I2C commands 125 | uint8_t gain; 126 | uint8_t zero; 127 | 128 | 129 | public: 130 | 131 | //CONSTRUCTORS 132 | LMP91000(); //tested 133 | 134 | //sets and gets MENB pin for enabling and disabling I2C commands 135 | void setMENB(uint8_t pin); 136 | uint8_t getMENB() const; 137 | 138 | //sets and gets pin for reading output of temperature sensor 139 | void setTempSensor(uint8_t pin); 140 | uint8_t getTempSensor() const; 141 | 142 | //reads and writes to LMP91000 via I2C 143 | void write(uint8_t reg, uint8_t data) const; 144 | uint8_t read(uint8_t reg) const; 145 | 146 | //enables and disables LMP91000 for I2C commands 147 | //default state is not ready 148 | void enable() const; 149 | void disable() const; 150 | boolean isReady() const; 151 | 152 | //locks and unlocks the transimpedance amplifier 153 | //and reference control registers for editing 154 | //default state is locked (read-only) 155 | void lock() const; 156 | void unlock() const; 157 | boolean isLocked() const; 158 | 159 | //sets the gain of the transimpedance amplifier 160 | void setGain(uint8_t gain); 161 | double getGain() const; 162 | 163 | //sets the load for compensating voltage differences 164 | //between working and reference electrodes 165 | void setRLoad(uint8_t load) const; 166 | 167 | //sets the source for the bias voltage for the 168 | //electrochemical cell 169 | void setRefSource(uint8_t source) const; 170 | void setIntRefSource() const; 171 | void setExtRefSource() const; 172 | 173 | //sets reference voltage for transimpedance amplifier 174 | void setIntZ(uint8_t intZ); 175 | double getIntZ() const; 176 | 177 | //sets bias voltage for electrochemical cell 178 | void setBiasSign(uint8_t sign) const; 179 | void setNegBias() const; 180 | void setPosBias() const; 181 | void setBias(uint8_t bias) const; 182 | void setBias(uint8_t bias, signed char sign) const; 183 | 184 | //enable and disable FET for deep sleep mode 185 | void setFET(uint8_t selection) const; 186 | void disableFET() const; 187 | void enableFET() const; 188 | 189 | //set operating modes for the LMP91000 190 | void setMode(uint8_t mode) const; 191 | void sleep() const; 192 | void setTwoLead() const; 193 | void standby() const; 194 | void setThreeLead() const; 195 | void measureCell() const; 196 | void getTemp() const; 197 | double getTemp(uint8_t sensor, double adc_ref, uint8_t adc_bits) const; 198 | 199 | //reading the output of the LMP91000 200 | uint16_t getOutput(uint8_t sensor) const; 201 | double getVoltage(uint16_t adcVal, double adc_ref, uint8_t adc_bits) const; 202 | double getCurrent(uint16_t adcVal, double adc_ref, uint8_t adc_bits) const; 203 | double getCurrent(uint16_t adcVal, double adc_ref, uint8_t adc_bits, 204 | double extGain) const; 205 | 206 | /* 207 | double getVoltage(uint8_t sensor, double adc_ref, uint8_t adc_bits) const; 208 | double getCurrent(uint8_t sensor, double adc_ref, uint8_t adc_bits) const; 209 | double getCurrent(uint8_t sensor, double adc_ref, uint8_t adc_bits, 210 | double extGain) const; 211 | */ 212 | 213 | }; 214 | 215 | #endif 216 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/Chronoamperometry/Chronoamperometry.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | LMP91000 pStat = LMP91000(); 5 | 6 | int16_t opVolt = 3300; //milliVolts if working with a 3.3V device 7 | uint8_t resolution = 10; //10-bits 8 | 9 | void setup() 10 | { 11 | Wire.begin(); 12 | Serial.begin(115200); 13 | 14 | pStat.standby(); 15 | delay(1000); //warm-up time for the gas sensor 16 | 17 | runAmp(2, 0, 5000, 66, 5000, 250, 5000, 80, 6); 18 | } 19 | 20 | 21 | void loop() 22 | { 23 | } 24 | 25 | 26 | //range = 12 is picoamperes 27 | //range = 9 is nanoamperes 28 | //range = 6 is microamperes 29 | //range = 3 is milliamperes 30 | void runAmp(uint8_t user_gain, int16_t pre_stepV, uint32_t quietTime, int16_t v1, 31 | uint32_t t1, int16_t v2, uint32_t t2, uint16_t samples, uint8_t range) 32 | { 33 | pStat.disableFET(); 34 | pStat.setGain(user_gain); 35 | pStat.setRLoad(0); 36 | pStat.setIntRefSource(); 37 | pStat.setIntZ(1); 38 | pStat.setThreeLead(); 39 | pStat.setBias(0); 40 | 41 | //Print column headers 42 | String current = ""; 43 | if(range == 12) current = "Current(pA)"; 44 | else if(range == 9) current = "Current(nA)"; 45 | else if(range == 6) current = "Current(uA)"; 46 | else if(range == 3) current = "Current(mA)"; 47 | else current = "SOME ERROR"; 48 | 49 | Serial.println("Voltage(mV),Time(ms)," + current); 50 | 51 | int16_t voltageArray[3] = {pre_stepV, v1, v2}; 52 | uint32_t timeArray[3] = {quietTime, t1, t2}; 53 | 54 | //i = 0 is pre-step voltage 55 | //i = 1 is first step potential 56 | //i = 2 is second step potential 57 | for(uint8_t i = 0; i < 3; i++) 58 | { 59 | //For pre-step voltage 60 | uint32_t fs = timeArray[i]/samples; 61 | voltageArray[i] = determineLMP91000Bias(voltageArray[i]); 62 | 63 | if(voltageArray[i] < 0) pStat.setNegBias(); 64 | else pStat.setPosBias(); 65 | 66 | unsigned long startTime = millis(); 67 | pStat.setBias(abs(voltageArray[i])); 68 | while(millis() - startTime < timeArray[i]) 69 | { 70 | Serial.print((uint16_t)(opVolt*TIA_BIAS[abs(voltageArray[i])]*(voltageArray[i]/abs(voltageArray[i])))); 71 | Serial.print(","); 72 | Serial.print(millis()); 73 | Serial.print(","); 74 | Serial.println(pow(10,range)*pStat.getCurrent(pStat.getOutput(A0), opVolt/1000.0, resolution)); 75 | delay(fs); 76 | } 77 | } 78 | 79 | //End at 0V 80 | pStat.setBias(0); 81 | } 82 | 83 | 84 | signed char determineLMP91000Bias(int16_t voltage) 85 | { 86 | signed char polarity = 0; 87 | if(voltage < 0) polarity = -1; 88 | else polarity = 1; 89 | 90 | int16_t v1 = 0; 91 | int16_t v2 = 0; 92 | 93 | voltage = abs(voltage); 94 | 95 | if(voltage == 0) return 0; 96 | 97 | for(int i = 0; i < NUM_TIA_BIAS-1; i++) 98 | { 99 | v1 = opVolt*TIA_BIAS[i]; 100 | v2 = opVolt*TIA_BIAS[i+1]; 101 | 102 | if(voltage == v1) return polarity*i; 103 | else if(voltage > v1 && voltage < v2) 104 | { 105 | if(abs(voltage-v1) < abs(voltage-v2)) return polarity*i; 106 | else return polarity*(i+1); 107 | } 108 | } 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/CyclicVoltammetry/CyclicVoltammetry.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | LMP91000 pstat = LMP91000(); 5 | 6 | int rate = 200; 7 | int settling_time = 50; 8 | 9 | void setup() 10 | { 11 | Wire.begin(); 12 | Serial.begin(9600); 13 | 14 | delay(50); 15 | pstat.standby(); 16 | delay(50); 17 | pstat.disableFET(); 18 | pstat.setGain(1); 19 | pstat.setRLoad(0); 20 | pstat.setExtRefSource(); 21 | pstat.setIntZ(1); 22 | pstat.setThreeLead(); 23 | delay(2000); //warm-up time for the gas sensor 24 | 25 | for (int j = 0; j < 3; j++) 26 | { 27 | pstat.setNegBias(); 28 | for (int i = 1; i < 11; i++) 29 | { 30 | pstat.setBias(i); 31 | //delay(50); 32 | delay(settling_time); 33 | Serial.print(i*-1); 34 | Serial.print(","); 35 | delay(1); 36 | Serial.println(analogRead(A0)); 37 | delay(rate); 38 | } 39 | for (int i = 10; i >= 0; i--) 40 | { 41 | pstat.setBias(i); 42 | //delay(50); 43 | delay(settling_time); 44 | Serial.print(i*-1); 45 | Serial.print(","); 46 | delay(1); 47 | Serial.println(analogRead(A0)); 48 | delay(rate); 49 | } 50 | pstat.setPosBias(); 51 | for (int i = 1; i < 11; i++) 52 | { 53 | pstat.setBias(i); 54 | //delay(50); 55 | delay(settling_time); 56 | Serial.print(i*1); 57 | Serial.print(","); 58 | delay(1); 59 | Serial.println(analogRead(A0)); 60 | delay(rate); 61 | } 62 | for (int i = 10; i >= 0; i--) 63 | { 64 | pstat.setBias(i); 65 | //delay(50); 66 | delay(settling_time); 67 | Serial.print(i*1); 68 | Serial.print(","); 69 | delay(1); 70 | Serial.println(analogRead(A0)); 71 | delay(rate); 72 | } 73 | } 74 | 75 | pstat.setBias(0); 76 | 77 | } 78 | 79 | void loop() { 80 | // put your main code here, to run repeatedly: 81 | 82 | } 83 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/For_Button_Cell/2019_04_09_1723_MiniStatButtonCellRevA_CODE/2019_04_09_1723_MiniStatButtonCellRevA_CODE.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include ; 4 | #include "pgmStrToRAM.h"; //SerialDebugger.println(freeMemory(), DEC); // print how much RAM is available. 5 | 6 | 7 | #if defined(ARDUINO_ARCH_SAMD) 8 | #define SerialDebugger SerialUSB 9 | #else 10 | #define SerialDebugger Serial 11 | #endif 12 | 13 | 14 | LMP91000 pStat = LMP91000(); 15 | 16 | 17 | const uint16_t opVolt = 3330; 18 | const uint8_t adcBits = 12; 19 | const double v_tolerance = 0.0075; 20 | const uint16_t dacResolution = pow(2,12)-1; //12-bit 21 | 22 | 23 | //analog input pins to read voltages 24 | const uint8_t LMP = A3; 25 | const uint8_t dac = A0; 26 | const uint8_t MENB = 5; 27 | 28 | 29 | void setup() 30 | { 31 | Wire.begin(); 32 | SerialDebugger.begin(115200); 33 | 34 | analogReadResolution(12); 35 | analogWriteResolution(12); 36 | 37 | 38 | pStat.setMENB(MENB); 39 | 40 | 41 | //enable the potentiostat 42 | delay(50); 43 | pStat.standby(); 44 | delay(50); 45 | initLMP(0); 46 | delay(2000); //warm-up time for the gas sensor 47 | } 48 | 49 | 50 | void loop() 51 | { 52 | SerialDebugger.println(F("Ready!")); 53 | 54 | //will hold the code here until a character is sent over the SerialDebugger port 55 | //this ensures the experiment will only run when initiated 56 | while(!SerialDebugger.available()); 57 | SerialDebugger.read(); 58 | 59 | 60 | //prints column headings 61 | SerialDebugger.println(F("Voltage,Zero,LMP")); 62 | 63 | 64 | //lmpGain, cycles, startV(mV), endV(mV), vertex1(mV), vertex2(mV), stepV(mV), rate (mV/s) 65 | //runCV(4, 2, 0, 0, 450, -200, 5, 100); //for FeCN 66 | runCV(0, 3, 0, 0, 50, -450, 2, 65); //for probe density 67 | //SerialDebugger.println("Backward Scan"); 68 | //runCV(4, 2, 0, 0, -500, 600, 2, 100); 69 | } 70 | 71 | 72 | void initLMP(uint8_t lmpGain) 73 | { 74 | pStat.disableFET(); 75 | pStat.setGain(lmpGain); 76 | pStat.setRLoad(0); 77 | pStat.setExtRefSource(); 78 | pStat.setIntZ(1); 79 | pStat.setThreeLead(); 80 | pStat.setBias(0); 81 | pStat.setPosBias(); 82 | 83 | setOutputsToZero(); 84 | } 85 | 86 | 87 | void setOutputsToZero() 88 | { 89 | analogWrite(dac,0); 90 | pStat.setBias(0); 91 | } 92 | 93 | 94 | void runCV(uint8_t lmpGain, uint8_t cycles, int16_t startV, 95 | int16_t endV, int16_t vertex1, int16_t vertex2, 96 | int16_t stepV, uint16_t rate) 97 | { 98 | initLMP(lmpGain); 99 | stepV = abs(stepV); 100 | rate = (1000.0*stepV)/rate; 101 | 102 | 103 | if(vertex1 > startV) runCVForward(cycles,startV,endV,vertex1,vertex2,stepV,rate); 104 | else runCVBackward(cycles,startV,endV,vertex1,vertex2,stepV,rate); 105 | } 106 | 107 | 108 | 109 | void runCVForward(uint8_t cycles, int16_t startV, int16_t endV, 110 | int16_t vertex1, int16_t vertex2, int16_t stepV, uint16_t rate) 111 | { 112 | int16_t j = startV; 113 | 114 | for(uint8_t i = 0; i < cycles; i++) 115 | { 116 | //j starts at startV 117 | for (j; j <= vertex1; j += stepV) 118 | { 119 | biasAndSample(j,rate); 120 | } 121 | j -= 2*stepV; 122 | 123 | 124 | //j starts right below the first vertex 125 | for (j; j >= vertex2; j -= stepV) 126 | { 127 | biasAndSample(j,rate); 128 | } 129 | j += 2*stepV; 130 | 131 | 132 | //j starts right above the second vertex 133 | for (j; j <= endV; j += stepV) 134 | { 135 | biasAndSample(j,rate); 136 | } 137 | j -= 2*stepV; 138 | 139 | } 140 | 141 | setOutputsToZero(); 142 | } 143 | 144 | 145 | void runCVBackward(uint8_t cycles, int16_t startV, int16_t endV, 146 | int16_t vertex1, int16_t vertex2, int16_t stepV, uint16_t rate) 147 | { 148 | int16_t j = startV; 149 | 150 | for(uint8_t i = 0; i < cycles; i++) 151 | { 152 | //j starts at startV 153 | for (j; j >= vertex1; j -= stepV) 154 | { 155 | biasAndSample(j,rate); 156 | } 157 | j += 2*stepV; 158 | 159 | 160 | //j starts right above vertex1 161 | for (j; j <= vertex2; j += stepV) 162 | { 163 | biasAndSample(j,rate); 164 | } 165 | j -= 2*stepV; 166 | 167 | 168 | //j starts right below vertex2 169 | for (j; j >= endV; j -= stepV) 170 | { 171 | biasAndSample(j,rate); 172 | } 173 | j += 2*stepV; 174 | 175 | } 176 | 177 | setOutputsToZero(); 178 | } 179 | 180 | 181 | 182 | void biasAndSample(int16_t voltage, uint16_t rate) 183 | { 184 | SerialDebugger.print(voltage); 185 | SerialDebugger.print(F(",")); 186 | 187 | setLMPBias(voltage); 188 | setVoltage(voltage); 189 | 190 | delay(rate); 191 | sampleOutputs(); 192 | SerialDebugger.println(); 193 | } 194 | 195 | 196 | 197 | void sampleOutputs() 198 | { 199 | //SerialDebugger.print(pStat.getCurrent(analogRead(LMP),opVolt,adcBits),8); 200 | SerialDebugger.print(pStat.getVoltage(analogRead(LMP), opVolt, adcBits)); 201 | } 202 | 203 | 204 | void setVoltage(int16_t voltage) 205 | { 206 | uint16_t dacVout = 1500; 207 | uint8_t bias_setting = 0; 208 | 209 | if(abs(voltage) < 15) voltage = 15*(voltage/abs(voltage)); 210 | 211 | int16_t setV = dacVout*TIA_BIAS[bias_setting]; 212 | voltage = abs(voltage); 213 | 214 | 215 | while(setV > voltage*(1+v_tolerance) || setV < voltage*(1-v_tolerance)) 216 | { 217 | if(bias_setting == 0) bias_setting = 1; 218 | 219 | dacVout = voltage/TIA_BIAS[bias_setting]; 220 | 221 | if (dacVout > opVolt) 222 | { 223 | bias_setting++; 224 | dacVout = 1500; 225 | 226 | if(bias_setting > NUM_TIA_BIAS) bias_setting = 0; 227 | } 228 | 229 | setV = dacVout*TIA_BIAS[bias_setting]; 230 | } 231 | 232 | 233 | pStat.setBias(bias_setting); 234 | analogWrite(dac,convertDACVoutToDACVal(dacVout)); 235 | 236 | SerialDebugger.print(dacVout*.5); 237 | SerialDebugger.print(F(",")); 238 | } 239 | 240 | 241 | 242 | //Convert the desired voltage 243 | uint16_t convertDACVoutToDACVal(uint16_t dacVout) 244 | { 245 | //return (dacVout-dacMin)*((double)dacResolution/dacSpan); 246 | return dacVout*((double)dacResolution/opVolt); 247 | } 248 | 249 | 250 | 251 | void setLMPBias(int16_t voltage) 252 | { 253 | signed char sign = (double)voltage/abs(voltage); 254 | 255 | if(sign < 0) pStat.setNegBias(); 256 | else if (sign > 0) pStat.setPosBias(); 257 | else {} //do nothing 258 | } 259 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/For_Button_Cell/2019_04_09_1723_MiniStatButtonCellRevA_CODE_MODDED/2019_04_09_1723_MiniStatButtonCellRevA_CODE_MODDED.ino: -------------------------------------------------------------------------------- 1 | //Standard Arduino Libraries 2 | #include 3 | 4 | //Custom External Libraries 5 | #include "MemoryFree.h" 6 | #include "pgmStrToRAM.h" //SerialDebugger.println(freeMemory(), DEC); // print how much RAM is available. 7 | 8 | //Custom Internal Libraries 9 | #include "LMP91000.h" 10 | #include "MiniStatAnalyst.h" 11 | 12 | 13 | #if defined(ARDUINO_ARCH_SAMD) 14 | #define SerialDebugger SerialUSB 15 | #else 16 | #define SerialDebugger Serial 17 | #endif 18 | 19 | 20 | LMP91000 pStat = LMP91000(); 21 | MiniStatAnalyst analyst = MiniStatAnalyst(); 22 | 23 | 24 | const uint16_t arr_samples = 750; 25 | uint16_t arr_cur_index = 0; 26 | float volts[arr_samples] = {}; 27 | float amps[arr_samples] = {}; 28 | 29 | 30 | const uint16_t opVolt = 3330; 31 | const uint8_t adcBits = 12; 32 | const float v_tolerance = 0.0075; 33 | const uint16_t dacResolution = pow(2,12)-1; //12-bit 34 | 35 | 36 | //analog input pins to read voltages 37 | const uint8_t LMP = A3; 38 | const uint8_t dac = A0; 39 | const uint8_t MENB = 5; 40 | 41 | unsigned long lastTime = 0; 42 | float RFB = 2200000; //in parallel with 15nF cap 43 | uint16_t dacVout = 1500; 44 | 45 | bool saveQueues = false; 46 | 47 | 48 | void setup() 49 | { 50 | Wire.begin(); 51 | SerialDebugger.begin(115200); 52 | while(!SerialDebugger); 53 | 54 | analogReadResolution(12); 55 | analogWriteResolution(12); 56 | 57 | 58 | pStat.setMENB(MENB); 59 | 60 | 61 | //enable the potentiostat 62 | delay(50); 63 | pStat.standby(); 64 | delay(50); 65 | initLMP(0); 66 | delay(2000); //warm-up time for the gas sensor 67 | } 68 | 69 | 70 | void loop() 71 | { 72 | SerialDebugger.println(F("Ready!")); 73 | 74 | //will hold the code here until a character is sent over the SerialDebugger port 75 | //this ensures the experiment will only run when initiated 76 | while(!SerialDebugger.available()); 77 | SerialDebugger.read(); 78 | 79 | 80 | //prints column headings 81 | SerialDebugger.println(F("Time(ms),Voltage,Zero,LMP,Current")); 82 | 83 | 84 | //lmpGain, cycles, startV(mV), endV(mV), vertex1(mV), vertex2(mV), stepV(mV), rate (mV/s) 85 | //runCV(4, 2, 0, 0, 450, -200, 5, 100); //for FeCN 86 | runCV(0, 3, 0, 0, 50, -450, 2, 50); //for probe density 87 | //SerialDebugger.println("Backward Scan"); 88 | //runCV(4, 2, 0, 0, -500, 600, 2, 100); 89 | 90 | float slope = 0; 91 | float intercept = 0; 92 | 93 | SerialDebugger.println(F("Ready!")); 94 | SerialDebugger.println(freeMemory(), DEC); 95 | while(!SerialDebugger.available()); 96 | SerialDebugger.read(); 97 | 98 | SerialDebugger.println(F("Voltage,Current")); 99 | for(uint16_t i = 0; i < arr_samples; i++) 100 | { 101 | SerialDebugger.print(volts[i]); 102 | SerialDebugger.print(F(",")); 103 | SerialDebugger.println(amps[i]); 104 | } 105 | 106 | analyst.calcTangentLine(amps, volts, slope, intercept, arr_samples); 107 | 108 | SerialDebugger.println(F("Ready!")); 109 | while(!SerialDebugger.available()); 110 | SerialDebugger.read(); 111 | 112 | float current = analyst.getPeakCurrent(amps, volts, slope, intercept, arr_samples); 113 | 114 | SerialDebugger.println(F("Ready!")); 115 | while(!SerialDebugger.available()); 116 | SerialDebugger.read(); 117 | 118 | SerialDebugger.print("slope: "); 119 | SerialDebugger.print(slope,5); 120 | SerialDebugger.print(", "); 121 | SerialDebugger.print("intercept: "); 122 | SerialDebugger.print(intercept,5); 123 | SerialDebugger.println(); 124 | SerialDebugger.print("current: "); 125 | SerialDebugger.print(current,5); 126 | SerialDebugger.println(); 127 | } 128 | 129 | 130 | void initLMP(uint8_t lmpGain) 131 | { 132 | pStat.disableFET(); 133 | pStat.setGain(lmpGain); 134 | pStat.setRLoad(0); 135 | pStat.setExtRefSource(); 136 | pStat.setIntZ(1); 137 | pStat.setThreeLead(); 138 | pStat.setBias(0); 139 | pStat.setPosBias(); 140 | 141 | setOutputsToZero(); 142 | } 143 | 144 | 145 | void setOutputsToZero() 146 | { 147 | analogWrite(dac,0); 148 | pStat.setBias(0); 149 | } 150 | 151 | 152 | void runCV(uint8_t lmpGain, uint8_t cycles, int16_t startV, 153 | int16_t endV, int16_t vertex1, int16_t vertex2, 154 | int16_t stepV, uint16_t rate) 155 | { 156 | initLMP(lmpGain); 157 | stepV = abs(stepV); 158 | rate = (1000.0*stepV)/rate; 159 | 160 | 161 | lastTime = millis(); 162 | if(vertex1 > startV) runCVForward(cycles,startV,endV,vertex1,vertex2,stepV,rate); 163 | else runCVBackward(cycles,startV,endV,vertex1,vertex2,stepV,rate); 164 | } 165 | 166 | 167 | 168 | void runCVForward(uint8_t cycles, int16_t startV, int16_t endV, 169 | int16_t vertex1, int16_t vertex2, int16_t stepV, uint16_t rate) 170 | { 171 | int16_t j = startV; 172 | 173 | for(uint8_t i = 0; i < cycles; i++) 174 | { 175 | if(i==cycles-2) saveQueues = true; 176 | else saveQueues = false; 177 | 178 | //j starts at startV 179 | for (j; j <= vertex1; j += stepV) 180 | { 181 | biasAndSample(j,rate); 182 | } 183 | j -= 2*stepV; 184 | 185 | 186 | //j starts right below the first vertex 187 | for (j; j >= vertex2; j -= stepV) 188 | { 189 | biasAndSample(j,rate); 190 | } 191 | j += 2*stepV; 192 | 193 | 194 | //j starts right above the second vertex 195 | for (j; j <= endV; j += stepV) 196 | { 197 | biasAndSample(j,rate); 198 | } 199 | j -= 2*stepV; 200 | 201 | } 202 | 203 | setOutputsToZero(); 204 | } 205 | 206 | 207 | void runCVBackward(uint8_t cycles, int16_t startV, int16_t endV, 208 | int16_t vertex1, int16_t vertex2, int16_t stepV, uint16_t rate) 209 | { 210 | int16_t j = startV; 211 | 212 | for(uint8_t i = 0; i < cycles; i++) 213 | { 214 | if(i==cycles-2) saveQueues = true; 215 | else saveQueues = false; 216 | 217 | //j starts at startV 218 | for (j; j >= vertex1; j -= stepV) 219 | { 220 | biasAndSample(j,rate); 221 | } 222 | j += 2*stepV; 223 | 224 | 225 | //j starts right above vertex1 226 | for (j; j <= vertex2; j += stepV) 227 | { 228 | biasAndSample(j,rate); 229 | } 230 | j -= 2*stepV; 231 | 232 | 233 | //j starts right below vertex2 234 | for (j; j >= endV; j -= stepV) 235 | { 236 | biasAndSample(j,rate); 237 | } 238 | j += 2*stepV; 239 | 240 | } 241 | 242 | setOutputsToZero(); 243 | } 244 | 245 | 246 | 247 | void biasAndSample(int16_t voltage, uint16_t rate) 248 | { 249 | SerialDebugger.print(millis()); 250 | SerialDebugger.print(F(",")); 251 | SerialDebugger.print(voltage); 252 | SerialDebugger.print(F(",")); 253 | 254 | setLMPBias(voltage); 255 | setVoltage(voltage); 256 | 257 | 258 | // //if(saveQueues && (voltage < -48)) saveQueues = true; 259 | // if(voltage < -48) saveQueues = true; 260 | // else saveQueues = false; 261 | 262 | if(saveQueues && (arr_cur_index < arr_samples) && (voltage < -48)) 263 | { 264 | volts[arr_cur_index] = voltage; 265 | arr_cur_index++; 266 | } 267 | 268 | 269 | while(millis() - lastTime < rate); 270 | // { 271 | // SerialDebugger.println("Am I stuck here?"); 272 | // } 273 | 274 | sampleOutputs(); 275 | SerialDebugger.println(); 276 | //arr_cur_index++; 277 | lastTime = millis(); 278 | } 279 | 280 | 281 | //void sampleOutputs() 282 | //{ 283 | // uint32_t num = 0; 284 | // uint8_t num_samples = 77; 285 | // 286 | // for(uint8_t i = 0; i < num_samples; i++) 287 | // { 288 | // num += analogRead(LMP); 289 | // } 290 | // 291 | // num = (float)num/num_samples; 292 | // 293 | // SerialDebugger.print(pStat.getVoltage(num, opVolt, adcBits)); 294 | //} 295 | 296 | 297 | void sampleOutputs() 298 | { 299 | float v1 = pStat.getVoltage(analogRead(LMP), opVolt, adcBits); 300 | float v2 = dacVout*.5; 301 | float current = (((v1-v2)/1000)/RFB)*pow(10,9); //scales to nA 302 | 303 | if(saveQueues && (arr_cur_index-1 < arr_samples)) 304 | { 305 | amps[arr_cur_index-1] = current; 306 | } 307 | 308 | 309 | SerialDebugger.print(v1); 310 | SerialDebugger.print(","); 311 | SerialDebugger.print(current,2); 312 | 313 | // float current = pStat.getCurrent(analogRead(LMP),opVolt,adcBit,RFB); 314 | // SerialDebugger.print(pStat.getVoltage(analogRead(LMP), opVolt, adcBits)); 315 | // SerialDebugger.print(","); 316 | // SerialDebugger.print(current,8); 317 | // amps.enqueue(current); 318 | } 319 | 320 | 321 | void setVoltage(int16_t voltage) 322 | { 323 | //uint16_t dacVout = 1500; 324 | dacVout = 1500; 325 | uint8_t bias_setting = 0; 326 | 327 | if(abs(voltage) < 15) voltage = 15*(voltage/abs(voltage)); 328 | 329 | int16_t setV = dacVout*TIA_BIAS[bias_setting]; 330 | voltage = abs(voltage); 331 | 332 | 333 | while(setV > voltage*(1+v_tolerance) || setV < voltage*(1-v_tolerance)) 334 | { 335 | if(bias_setting == 0) bias_setting = 1; 336 | 337 | dacVout = voltage/TIA_BIAS[bias_setting]; 338 | 339 | if (dacVout > opVolt) 340 | { 341 | bias_setting++; 342 | dacVout = 1500; 343 | 344 | if(bias_setting > NUM_TIA_BIAS) bias_setting = 0; 345 | } 346 | 347 | setV = dacVout*TIA_BIAS[bias_setting]; 348 | } 349 | 350 | 351 | pStat.setBias(bias_setting); 352 | analogWrite(dac,convertDACVoutToDACVal(dacVout)); 353 | 354 | SerialDebugger.print(dacVout*.5); 355 | SerialDebugger.print(F(",")); 356 | } 357 | 358 | 359 | 360 | //Convert the desired voltage 361 | uint16_t convertDACVoutToDACVal(uint16_t dacVout) 362 | { 363 | //return (dacVout-dacMin)*((float)dacResolution/dacSpan); 364 | return dacVout*((float)dacResolution/opVolt); 365 | } 366 | 367 | 368 | 369 | void setLMPBias(int16_t voltage) 370 | { 371 | signed char sign = (float)voltage/abs(voltage); 372 | 373 | if(sign < 0) pStat.setNegBias(); 374 | else if (sign > 0) pStat.setPosBias(); 375 | else {} //do nothing 376 | } 377 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/For_Button_Cell/CyclicVoltammetry-Button_Cell-for-probe_density/CyclicVoltammetry-Button_Cell-for-probe_density.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include ; 6 | #include "pgmStrToRAM.h"; //SerialUSB.println(freeMemory(), DEC); // print how much RAM is available. 7 | 8 | 9 | #if defined(ARDUINO_ARCH_SAMD) 10 | #define SerialDebugger SerialUSB 11 | #else 12 | #define SerialDebugger Serial 13 | #endif 14 | 15 | 16 | 17 | LMP91000 pStat = LMP91000(); 18 | 19 | 20 | const uint16_t opVolt = 3280; 21 | const uint8_t adcBits = 10; 22 | const double v_tolerance = 0.0075; 23 | //const double extGain = 3900000; 24 | const double extGain = 1000000; 25 | 26 | 27 | //const uint16_t dacMin = 0; //0V 28 | //const uint16_t dacMax = opVolt; //Vdd 29 | //const uint16_t dacSpan = opVolt; //Vdd 30 | const uint16_t dacResolution = pow(2,16)-1; //16-bit 31 | 32 | 33 | bool debug = true; 34 | 35 | 36 | //analog input pins to read voltages 37 | const uint8_t LMP = A2; 38 | const uint8_t C1 = A0; 39 | const uint8_t C2 = A1; 40 | const uint8_t DACRead = A3; 41 | const uint8_t diffAmp = A6; 42 | const uint8_t INA = A7; 43 | 44 | 45 | void setup() 46 | { 47 | Wire.begin(); 48 | SerialDebugger.begin(115200); 49 | 50 | //enable the potentiostat 51 | delay(50); 52 | pStat.standby(); 53 | delay(50); 54 | initLMP(0); 55 | delay(2000); //warm-up time for the gas sensor 56 | 57 | } 58 | 59 | void loop() 60 | { 61 | SerialDebugger.println(F("Ready!")); 62 | 63 | //will hold the code here until a character is sent over the SerialDebugger port 64 | //this ensures the experiment will only run when initiated 65 | while(!SerialDebugger.available()); 66 | SerialDebugger.read(); 67 | 68 | 69 | //prints column headings 70 | // if(debug) SerialDebugger.println(F("Time(ms),Voltage,Sign,Bias Index,Bias,dacOut,DueDACValue,DAC Read,LMP,C1,C2")); 71 | // else SerialDebugger.println(F("Time(ms),Voltage,LMP,C1,C2")); 72 | 73 | //prints column headings 74 | if(debug) SerialDebugger.println(F("Time(ms),Voltage,Sign,Bias Index,Bias,dacOut,DueDACValue,DAC Read,LMP,C1,C2,DiffAmp,INA")); 75 | else SerialDebugger.println(F("Time(ms),Voltage,LMP,C1,C2")); 76 | 77 | 78 | //lmpGain, cycles, startV(mV), endV(mV), vertex1(mV), vertex2(mV), stepV(mV), rate (mV/s) 79 | runCV(0, 3, 0, 0, 50, -450, 2, 65); 80 | //SerialDebugger.println("Backward Scan"); 81 | //runCV(4, 2, 0, 0, -500, 600, 2, 100); 82 | } 83 | 84 | 85 | void initLMP(uint8_t lmpGain) 86 | { 87 | pStat.disableFET(); 88 | pStat.setGain(lmpGain); 89 | pStat.setRLoad(0); 90 | pStat.setExtRefSource(); 91 | pStat.setIntZ(1); 92 | pStat.setThreeLead(); 93 | pStat.setBias(0); 94 | pStat.setPosBias(); 95 | 96 | setOutputsToZero(); 97 | } 98 | 99 | 100 | void setOutputsToZero() 101 | { 102 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, 0); 103 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 104 | pStat.setBias(0); 105 | } 106 | 107 | 108 | void runCV(uint8_t lmpGain, uint8_t cycles, int16_t startV, 109 | int16_t endV, int16_t vertex1, int16_t vertex2, 110 | int16_t stepV, uint16_t rate) 111 | { 112 | initLMP(lmpGain); 113 | stepV = abs(stepV); 114 | rate = (1000.0*stepV)/rate; 115 | 116 | 117 | if(vertex1 > startV) runCVForward(cycles,startV,endV,vertex1,vertex2,stepV,rate); 118 | else runCVBackward(cycles,startV,endV,vertex1,vertex2,stepV,rate); 119 | } 120 | 121 | 122 | 123 | void runCVForward(uint8_t cycles, int16_t startV, int16_t endV, 124 | int16_t vertex1, int16_t vertex2, int16_t stepV, uint16_t rate) 125 | { 126 | // SerialDebugger.println("Forward"); 127 | // SerialDebugger.println(startV); 128 | // SerialDebugger.println(endV); 129 | // SerialDebugger.println(vertex1); 130 | // SerialDebugger.println(vertex2); 131 | // SerialDebugger.println(stepV); 132 | // while(!SerialDebugger.available()); 133 | // SerialDebugger.println(rate); 134 | 135 | int16_t j = startV; 136 | 137 | for(uint8_t i = 0; i < cycles; i++) 138 | { 139 | //j starts at startV 140 | for (j; j <= vertex1; j += stepV) 141 | { 142 | biasAndSample(j,rate); 143 | // SerialDebugger.println(); 144 | // SerialDebugger.println(); 145 | // SerialDebugger.println(freeMemory(), DEC); // print how much RAM is available. 146 | } 147 | j -= 2*stepV; 148 | 149 | 150 | //j starts right below the first vertex 151 | for (j; j >= vertex2; j -= stepV) 152 | { 153 | biasAndSample(j,rate); 154 | } 155 | j += 2*stepV; 156 | 157 | 158 | //j starts right above the second vertex 159 | for (j; j <= endV; j += stepV) 160 | { 161 | biasAndSample(j,rate); 162 | } 163 | j -= 2*stepV; 164 | 165 | } 166 | 167 | setOutputsToZero(); 168 | } 169 | 170 | 171 | void runCVBackward(uint8_t cycles, int16_t startV, int16_t endV, 172 | int16_t vertex1, int16_t vertex2, int16_t stepV, uint16_t rate) 173 | { 174 | int16_t j = startV; 175 | 176 | for(uint8_t i = 0; i < cycles; i++) 177 | { 178 | //j starts at startV 179 | for (j; j >= vertex1; j -= stepV) 180 | { 181 | biasAndSample(j,rate); 182 | } 183 | j += 2*stepV; 184 | 185 | 186 | //j starts right above vertex1 187 | for (j; j <= vertex2; j += stepV) 188 | { 189 | biasAndSample(j,rate); 190 | } 191 | j -= 2*stepV; 192 | 193 | 194 | //j starts right below vertex2 195 | for (j; j >= endV; j -= stepV) 196 | { 197 | biasAndSample(j,rate); 198 | } 199 | j += 2*stepV; 200 | 201 | } 202 | 203 | setOutputsToZero(); 204 | } 205 | 206 | 207 | 208 | void biasAndSample(int16_t voltage, uint16_t rate) 209 | { 210 | SerialDebugger.print(millis()); 211 | SerialDebugger.print(F(",")); 212 | SerialDebugger.print(voltage); 213 | SerialDebugger.print(F(",")); 214 | 215 | setLMPBias(voltage); 216 | setVoltage(voltage); 217 | 218 | delay(rate); 219 | sampleOutputs(); 220 | testingDiffAndINAAmps(); 221 | SerialDebugger.println(); 222 | } 223 | 224 | 225 | 226 | void sampleOutputs() 227 | { 228 | // SerialDebugger.print(analogRead(DACRead)); 229 | // SerialDebugger.print(F(",")); 230 | // SerialDebugger.print(analogRead(LMP)); 231 | // SerialDebugger.print(F(",")); 232 | // SerialDebugger.print(analogRead(C1)); 233 | // SerialDebugger.print(F(",")); 234 | // SerialDebugger.print(analogRead(C2)); 235 | // 236 | // 237 | // SerialDebugger.print(analogRead(DACRead)); 238 | // SerialDebugger.print(F(",")); 239 | // SerialDebugger.print(analogRead(LMP)); 240 | // SerialDebugger.print(F(",")); 241 | // SerialDebugger.print(analogRead(C1)); 242 | // SerialDebugger.print(F(",")); 243 | // SerialDebugger.print(analogRead(C2)); 244 | 245 | 246 | SerialDebugger.print(pStat.getVoltage(analogRead(DACRead),opVolt,adcBits),1); 247 | SerialDebugger.print(F(",")); 248 | SerialDebugger.print(pStat.getCurrent(analogRead(LMP),opVolt,adcBits,extGain),8); 249 | SerialDebugger.print(F(",")); 250 | SerialDebugger.print(pStat.getCurrent(analogRead(C1),opVolt,adcBits,extGain),8); 251 | SerialDebugger.print(F(",")); 252 | SerialDebugger.print(pStat.getCurrent(analogRead(C2),opVolt,adcBits,extGain),8); 253 | } 254 | 255 | 256 | void testingDiffAndINAAmps() 257 | { 258 | // SerialDebugger.print(F(",")); 259 | // SerialDebugger.print(analogRead(diffAmp)); 260 | // SerialDebugger.print(F(",")); 261 | // SerialDebugger.print(analogRead(INA)); 262 | 263 | SerialDebugger.print(F(",")); 264 | SerialDebugger.print(pStat.getCurrent(analogRead(diffAmp),opVolt,adcBits,extGain),8); 265 | SerialDebugger.print(F(",")); 266 | SerialDebugger.print(pStat.getCurrent(analogRead(INA),opVolt,adcBits,extGain),8); 267 | } 268 | 269 | 270 | 271 | void setVoltage(int16_t voltage) 272 | { 273 | uint16_t dacVout = 1500; 274 | uint8_t bias_setting = 0; 275 | 276 | if(abs(voltage) < 15) voltage = 15*(voltage/abs(voltage)); 277 | 278 | int16_t setV = dacVout*TIA_BIAS[bias_setting]; 279 | voltage = abs(voltage); 280 | 281 | 282 | while(setV > voltage*(1+v_tolerance) || setV < voltage*(1-v_tolerance)) 283 | { 284 | if(bias_setting == 0) bias_setting = 1; 285 | 286 | dacVout = voltage/TIA_BIAS[bias_setting]; 287 | 288 | if (dacVout > opVolt) 289 | { 290 | bias_setting++; 291 | dacVout = 1500; 292 | 293 | if(bias_setting > NUM_TIA_BIAS) bias_setting = 0; 294 | } 295 | 296 | setV = dacVout*TIA_BIAS[bias_setting]; 297 | } 298 | 299 | 300 | pStat.setBias(bias_setting); 301 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, convertDACVoutToDACVal(dacVout)); 302 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 303 | 304 | 305 | if(debug) 306 | { 307 | SerialDebugger.print(bias_setting); 308 | SerialDebugger.print(F(",")); 309 | SerialDebugger.print(TIA_BIAS[bias_setting]); 310 | SerialDebugger.print(F(",")); 311 | SerialDebugger.print(dacVout); 312 | SerialDebugger.print(F(",")); 313 | //SerialDebugger.print(convertDACVoutToDACVal(dacVout)); 314 | SerialDebugger.print(convertDACVoutToDACVal(dacVout) >> 2); 315 | SerialDebugger.print(F(",")); 316 | } 317 | } 318 | 319 | 320 | 321 | //Convert the desired voltage 322 | uint16_t convertDACVoutToDACVal(uint16_t dacVout) 323 | { 324 | //return (dacVout-dacMin)*((double)dacResolution/dacSpan); 325 | return dacVout*((double)dacResolution/opVolt); 326 | } 327 | 328 | 329 | 330 | void setLMPBias(int16_t voltage) 331 | { 332 | signed char sign = (double)voltage/abs(voltage); 333 | 334 | if(sign < 0) pStat.setNegBias(); 335 | else if (sign > 0) pStat.setPosBias(); 336 | else {} //do nothing 337 | 338 | if(debug) 339 | { 340 | SerialDebugger.print(sign); 341 | SerialDebugger.print(F(",")); 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/For_Button_Cell/EIS/EIS.ino: -------------------------------------------------------------------------------- 1 | //Standard Arduino Libraries 2 | #include 3 | 4 | //Custom External Libraries 5 | #include "MemoryFree.h" 6 | #include "pgmStrToRAM.h" //SerialDebugger.println(freeMemory(), DEC); // print how much RAM is available. 7 | 8 | //Custom Internal Libraries 9 | #include "LMP91000.h" 10 | #include "MiniStatAnalyst.h" 11 | 12 | 13 | #if defined(ARDUINO_ARCH_SAMD) 14 | #define SerialDebugger SerialUSB 15 | #else 16 | #define SerialDebugger Serial 17 | #endif 18 | 19 | 20 | LMP91000 pStat = LMP91000(); 21 | MiniStatAnalyst analyst = MiniStatAnalyst(); 22 | 23 | 24 | const uint16_t arr_samples = 750; 25 | uint16_t arr_cur_index = 0; 26 | float volts[arr_samples] = {}; 27 | float amps[arr_samples] = {}; 28 | 29 | 30 | const uint16_t opVolt = 3330; 31 | const uint8_t adcBits = 12; 32 | const float v_tolerance = 0.0075; 33 | const uint16_t dacResolution = pow(2,12)-1; //12-bit 34 | 35 | 36 | //analog input pins to read voltages 37 | const uint8_t LMP = A3; 38 | const uint8_t dac = A0; 39 | const uint8_t MENB = 5; 40 | 41 | unsigned long lastTime = 0; 42 | float RFB = 2200000; //in parallel with 15nF cap 43 | uint16_t dacVout = 1500; 44 | 45 | bool saveQueues = false; 46 | 47 | 48 | void setup() 49 | { 50 | Wire.begin(); 51 | SerialDebugger.begin(115200); 52 | while(!SerialDebugger); 53 | 54 | analogReadResolution(12); 55 | analogWriteResolution(12); 56 | 57 | 58 | pStat.setMENB(MENB); 59 | 60 | 61 | //enable the potentiostat 62 | delay(50); 63 | pStat.standby(); 64 | delay(50); 65 | initLMP(0); 66 | delay(2000); //warm-up time for the gas sensor 67 | } 68 | 69 | 70 | void loop() 71 | { 72 | SerialDebugger.println(F("Ready!")); 73 | 74 | //will hold the code here until a character is sent over the SerialDebugger port 75 | //this ensures the experiment will only run when initiated 76 | while(!SerialDebugger.available()); 77 | SerialDebugger.read(); 78 | 79 | 80 | //prints column headings 81 | SerialDebugger.println(F("Time(ms),Voltage,Zero,LMP,Current")); 82 | 83 | 84 | //lmpGain, cycles, startV(mV), endV(mV), vertex1(mV), vertex2(mV), stepV(mV), rate (mV/s) 85 | //runCV(4, 2, 0, 0, 450, -200, 5, 100); //for FeCN 86 | runCV(0, 3, 0, 0, 50, -450, 2, 50); //for probe density 87 | //SerialDebugger.println("Backward Scan"); 88 | //runCV(4, 2, 0, 0, -500, 600, 2, 100); 89 | 90 | float slope = 0; 91 | float intercept = 0; 92 | 93 | SerialDebugger.println(F("Ready!")); 94 | SerialDebugger.println(freeMemory(), DEC); 95 | while(!SerialDebugger.available()); 96 | SerialDebugger.read(); 97 | 98 | SerialDebugger.println(F("Voltage,Current")); 99 | for(uint16_t i = 0; i < arr_samples; i++) 100 | { 101 | SerialDebugger.print(volts[i]); 102 | SerialDebugger.print(F(",")); 103 | SerialDebugger.println(amps[i]); 104 | } 105 | 106 | analyst.calcTangentLine(amps, volts, slope, intercept, arr_samples); 107 | 108 | SerialDebugger.println(F("Ready!")); 109 | while(!SerialDebugger.available()); 110 | SerialDebugger.read(); 111 | 112 | float current = analyst.getPeakCurrent(amps, volts, slope, intercept, arr_samples); 113 | 114 | SerialDebugger.println(F("Ready!")); 115 | while(!SerialDebugger.available()); 116 | SerialDebugger.read(); 117 | 118 | SerialDebugger.print("slope: "); 119 | SerialDebugger.print(slope,5); 120 | SerialDebugger.print(", "); 121 | SerialDebugger.print("intercept: "); 122 | SerialDebugger.print(intercept,5); 123 | SerialDebugger.println(); 124 | SerialDebugger.print("current: "); 125 | SerialDebugger.print(current,5); 126 | SerialDebugger.println(); 127 | } 128 | 129 | 130 | void initLMP(uint8_t lmpGain) 131 | { 132 | pStat.disableFET(); 133 | pStat.setGain(lmpGain); 134 | pStat.setRLoad(0); 135 | pStat.setExtRefSource(); 136 | pStat.setIntZ(1); 137 | pStat.setThreeLead(); 138 | pStat.setBias(0); 139 | pStat.setPosBias(); 140 | 141 | setOutputsToZero(); 142 | } 143 | 144 | 145 | void setOutputsToZero() 146 | { 147 | analogWrite(dac,0); 148 | pStat.setBias(0); 149 | } 150 | 151 | 152 | void runCV(uint8_t lmpGain, uint8_t cycles, int16_t startV, 153 | int16_t endV, int16_t vertex1, int16_t vertex2, 154 | int16_t stepV, uint16_t rate) 155 | { 156 | initLMP(lmpGain); 157 | stepV = abs(stepV); 158 | rate = (1000.0*stepV)/rate; 159 | 160 | 161 | lastTime = millis(); 162 | if(vertex1 > startV) runCVForward(cycles,startV,endV,vertex1,vertex2,stepV,rate); 163 | else runCVBackward(cycles,startV,endV,vertex1,vertex2,stepV,rate); 164 | } 165 | 166 | 167 | 168 | void runCVForward(uint8_t cycles, int16_t startV, int16_t endV, 169 | int16_t vertex1, int16_t vertex2, int16_t stepV, uint16_t rate) 170 | { 171 | int16_t j = startV; 172 | 173 | for(uint8_t i = 0; i < cycles; i++) 174 | { 175 | if(i==cycles-2) saveQueues = true; 176 | else saveQueues = false; 177 | 178 | //j starts at startV 179 | for (j; j <= vertex1; j += stepV) 180 | { 181 | biasAndSample(j,rate); 182 | } 183 | j -= 2*stepV; 184 | 185 | 186 | //j starts right below the first vertex 187 | for (j; j >= vertex2; j -= stepV) 188 | { 189 | biasAndSample(j,rate); 190 | } 191 | j += 2*stepV; 192 | 193 | 194 | //j starts right above the second vertex 195 | for (j; j <= endV; j += stepV) 196 | { 197 | biasAndSample(j,rate); 198 | } 199 | j -= 2*stepV; 200 | 201 | } 202 | 203 | setOutputsToZero(); 204 | } 205 | 206 | 207 | void runCVBackward(uint8_t cycles, int16_t startV, int16_t endV, 208 | int16_t vertex1, int16_t vertex2, int16_t stepV, uint16_t rate) 209 | { 210 | int16_t j = startV; 211 | 212 | for(uint8_t i = 0; i < cycles; i++) 213 | { 214 | if(i==cycles-2) saveQueues = true; 215 | else saveQueues = false; 216 | 217 | //j starts at startV 218 | for (j; j >= vertex1; j -= stepV) 219 | { 220 | biasAndSample(j,rate); 221 | } 222 | j += 2*stepV; 223 | 224 | 225 | //j starts right above vertex1 226 | for (j; j <= vertex2; j += stepV) 227 | { 228 | biasAndSample(j,rate); 229 | } 230 | j -= 2*stepV; 231 | 232 | 233 | //j starts right below vertex2 234 | for (j; j >= endV; j -= stepV) 235 | { 236 | biasAndSample(j,rate); 237 | } 238 | j += 2*stepV; 239 | 240 | } 241 | 242 | setOutputsToZero(); 243 | } 244 | 245 | 246 | 247 | void biasAndSample(int16_t voltage, uint16_t rate) 248 | { 249 | SerialDebugger.print(millis()); 250 | SerialDebugger.print(F(",")); 251 | SerialDebugger.print(voltage); 252 | SerialDebugger.print(F(",")); 253 | 254 | setLMPBias(voltage); 255 | setVoltage(voltage); 256 | 257 | 258 | // //if(saveQueues && (voltage < -48)) saveQueues = true; 259 | // if(voltage < -48) saveQueues = true; 260 | // else saveQueues = false; 261 | 262 | if(saveQueues && (arr_cur_index < arr_samples) && (voltage < -48)) 263 | { 264 | volts[arr_cur_index] = voltage; 265 | arr_cur_index++; 266 | } 267 | 268 | 269 | while(millis() - lastTime < rate); 270 | // { 271 | // SerialDebugger.println("Am I stuck here?"); 272 | // } 273 | 274 | sampleOutputs(); 275 | SerialDebugger.println(); 276 | //arr_cur_index++; 277 | lastTime = millis(); 278 | } 279 | 280 | 281 | //void sampleOutputs() 282 | //{ 283 | // uint32_t num = 0; 284 | // uint8_t num_samples = 77; 285 | // 286 | // for(uint8_t i = 0; i < num_samples; i++) 287 | // { 288 | // num += analogRead(LMP); 289 | // } 290 | // 291 | // num = (float)num/num_samples; 292 | // 293 | // SerialDebugger.print(pStat.getVoltage(num, opVolt, adcBits)); 294 | //} 295 | 296 | 297 | void sampleOutputs() 298 | { 299 | float v1 = pStat.getVoltage(analogRead(LMP), opVolt, adcBits); 300 | float v2 = dacVout*.5; 301 | float current = (((v1-v2)/1000)/RFB)*pow(10,9); //scales to nA 302 | 303 | if(saveQueues && (arr_cur_index-1 < arr_samples)) 304 | { 305 | amps[arr_cur_index-1] = current; 306 | } 307 | 308 | 309 | SerialDebugger.print(v1); 310 | SerialDebugger.print(","); 311 | SerialDebugger.print(current,2); 312 | 313 | // float current = pStat.getCurrent(analogRead(LMP),opVolt,adcBit,RFB); 314 | // SerialDebugger.print(pStat.getVoltage(analogRead(LMP), opVolt, adcBits)); 315 | // SerialDebugger.print(","); 316 | // SerialDebugger.print(current,8); 317 | // amps.enqueue(current); 318 | } 319 | 320 | 321 | void setVoltage(int16_t voltage) 322 | { 323 | //uint16_t dacVout = 1500; 324 | dacVout = 1500; 325 | uint8_t bias_setting = 0; 326 | 327 | if(abs(voltage) < 15) voltage = 15*(voltage/abs(voltage)); 328 | 329 | int16_t setV = dacVout*TIA_BIAS[bias_setting]; 330 | voltage = abs(voltage); 331 | 332 | 333 | while(setV > voltage*(1+v_tolerance) || setV < voltage*(1-v_tolerance)) 334 | { 335 | if(bias_setting == 0) bias_setting = 1; 336 | 337 | dacVout = voltage/TIA_BIAS[bias_setting]; 338 | 339 | if (dacVout > opVolt) 340 | { 341 | bias_setting++; 342 | dacVout = 1500; 343 | 344 | if(bias_setting > NUM_TIA_BIAS) bias_setting = 0; 345 | } 346 | 347 | setV = dacVout*TIA_BIAS[bias_setting]; 348 | } 349 | 350 | 351 | pStat.setBias(bias_setting); 352 | analogWrite(dac,convertDACVoutToDACVal(dacVout)); 353 | 354 | SerialDebugger.print(dacVout*.5); 355 | SerialDebugger.print(F(",")); 356 | } 357 | 358 | 359 | 360 | //Convert the desired voltage 361 | uint16_t convertDACVoutToDACVal(uint16_t dacVout) 362 | { 363 | //return (dacVout-dacMin)*((float)dacResolution/dacSpan); 364 | return dacVout*((float)dacResolution/opVolt); 365 | } 366 | 367 | 368 | 369 | void setLMPBias(int16_t voltage) 370 | { 371 | signed char sign = (float)voltage/abs(voltage); 372 | 373 | if(sign < 0) pStat.setNegBias(); 374 | else if (sign > 0) pStat.setPosBias(); 375 | else {} //do nothing 376 | } 377 | 378 | 379 | 380 | //bias potential 381 | //AC signal 382 | //points per decade 383 | //repititions 384 | //settling cycles 385 | //start frequency 386 | //end frequency 387 | void runEIS(double startF, double ) 388 | { 389 | 390 | } 391 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/For_Button_Cell/sketch_jun05c/sketch_jun05c.ino: -------------------------------------------------------------------------------- 1 | #include "MiniStatAnalyst.h" 2 | #include "MemoryFree.h" 3 | #include "pgmStrToRAM.h" //SerialDebugger.println(freeMemory(), DEC); // print how much RAM is available. 4 | 5 | 6 | #if defined(ARDUINO_ARCH_SAMD) 7 | #define SerialDebugger SerialUSB 8 | #else 9 | #define SerialDebugger Serial 10 | #endif 11 | 12 | 13 | MiniStatAnalyst analyst = MiniStatAnalyst(); 14 | 15 | const uint16_t num_array = 300; 16 | 17 | //float amps[num_array] = { 18 | //-23, 19 | //-24, 20 | //-25, 21 | //-26, 22 | //-27, 23 | //-28, 24 | //-29, 25 | //-28, 26 | //-29, 27 | //-28, 28 | //-30, 29 | //-32, 30 | //-29, 31 | //-30, 32 | //-29, 33 | //-32, 34 | //-32, 35 | //-32, 36 | //-33, 37 | //-32, 38 | //-34, 39 | //-34, 40 | //-33, 41 | //-33, 42 | //-32, 43 | //-34, 44 | //-36, 45 | //-35, 46 | //-34, 47 | //-36, 48 | //-36, 49 | //-37, 50 | //-36, 51 | //-35, 52 | //-36, 53 | //-36, 54 | //-37, 55 | //-37, 56 | //-38, 57 | //-38, 58 | //-38, 59 | //-37, 60 | //-39, 61 | //-39, 62 | //-39, 63 | //-38, 64 | //-39, 65 | //-39, 66 | //-41, 67 | //-40, 68 | //-42, 69 | //-41, 70 | //-41, 71 | //-43, 72 | //-43, 73 | //-42, 74 | //-44, 75 | //-44, 76 | //-44, 77 | //-47, 78 | //-46, 79 | //-46, 80 | //-46, 81 | //-46, 82 | //-48, 83 | //-47, 84 | //-48, 85 | //-45, 86 | //-49, 87 | //-49, 88 | //-49, 89 | //-50, 90 | //-50, 91 | //-50, 92 | //-50, 93 | //-51, 94 | //-59, 95 | //-51, 96 | //-50, 97 | //-52, 98 | //-53, 99 | //-53, 100 | //-53, 101 | //-53, 102 | //-53, 103 | //-54, 104 | //-55, 105 | //-56, 106 | //-54, 107 | //-54, 108 | //-56, 109 | //-57, 110 | //-55, 111 | //-57, 112 | //-57, 113 | //-59, 114 | //-58, 115 | //-59, 116 | //-57, 117 | //-61, 118 | //-61, 119 | //-60, 120 | //-62, 121 | //-61, 122 | //-63, 123 | //-64, 124 | //-64, 125 | //-64, 126 | //-66, 127 | //-64, 128 | //-66, 129 | //-68, 130 | //-66, 131 | //-68, 132 | //-67, 133 | //-69, 134 | //-70, 135 | //-70, 136 | //-71, 137 | //-73, 138 | //-73, 139 | //-73, 140 | //-75, 141 | //-74, 142 | //-74, 143 | //-76, 144 | //-77, 145 | //-78, 146 | //-76, 147 | //-79, 148 | //-82, 149 | //-82, 150 | //-83, 151 | //-84, 152 | //-84, 153 | //-86, 154 | //-87, 155 | //-90, 156 | //-87, 157 | //-88, 158 | //-91, 159 | //-92, 160 | //-92, 161 | //-94, 162 | //-92, 163 | //-90, 164 | //-92, 165 | //-94, 166 | //-93, 167 | //-95, 168 | //-95, 169 | //-96, 170 | //-96, 171 | //-95, 172 | //-96, 173 | //-95, 174 | //-95, 175 | //-96, 176 | //-98, 177 | //-95, 178 | //-94, 179 | //-95, 180 | //-94, 181 | //-96, 182 | //-97, 183 | //-95, 184 | //-95, 185 | //-95, 186 | //-96, 187 | //-95, 188 | //-95, 189 | //-94, 190 | //-94, 191 | //-96, 192 | //-95, 193 | //-95, 194 | //-94, 195 | //-95, 196 | //-95, 197 | //-94, 198 | //-95, 199 | //-96, 200 | //-96, 201 | //-98, 202 | //-98, 203 | //-99, 204 | //-99, 205 | //-100, 206 | //-99, 207 | //-103, 208 | //-102 209 | //}; 210 | // 211 | // 212 | //int16_t volts[num_array] = { 213 | //-44, 214 | //-46, 215 | //-48, 216 | //-50, 217 | //-52, 218 | //-54, 219 | //-56, 220 | //-58, 221 | //-60, 222 | //-62, 223 | //-64, 224 | //-66, 225 | //-68, 226 | //-70, 227 | //-72, 228 | //-74, 229 | //-76, 230 | //-78, 231 | //-80, 232 | //-82, 233 | //-84, 234 | //-86, 235 | //-88, 236 | //-90, 237 | //-92, 238 | //-94, 239 | //-96, 240 | //-98, 241 | //-100, 242 | //-102, 243 | //-104, 244 | //-106, 245 | //-108, 246 | //-110, 247 | //-112, 248 | //-114, 249 | //-116, 250 | //-118, 251 | //-120, 252 | //-122, 253 | //-124, 254 | //-126, 255 | //-128, 256 | //-130, 257 | //-132, 258 | //-134, 259 | //-136, 260 | //-138, 261 | //-140, 262 | //-142, 263 | //-144, 264 | //-146, 265 | //-148, 266 | //-150, 267 | //-152, 268 | //-154, 269 | //-156, 270 | //-158, 271 | //-160, 272 | //-162, 273 | //-164, 274 | //-166, 275 | //-168, 276 | //-170, 277 | //-172, 278 | //-174, 279 | //-176, 280 | //-178, 281 | //-180, 282 | //-182, 283 | //-184, 284 | //-186, 285 | //-188, 286 | //-190, 287 | //-192, 288 | //-194, 289 | //-196, 290 | //-198, 291 | //-200, 292 | //-202, 293 | //-204, 294 | //-206, 295 | //-208, 296 | //-210, 297 | //-212, 298 | //-214, 299 | //-216, 300 | //-218, 301 | //-220, 302 | //-222, 303 | //-224, 304 | //-226, 305 | //-228, 306 | //-230, 307 | //-232, 308 | //-234, 309 | //-236, 310 | //-238, 311 | //-240, 312 | //-242, 313 | //-244, 314 | //-246, 315 | //-248, 316 | //-250, 317 | //-252, 318 | //-254, 319 | //-256, 320 | //-258, 321 | //-260, 322 | //-262, 323 | //-264, 324 | //-266, 325 | //-268, 326 | //-270, 327 | //-272, 328 | //-274, 329 | //-276, 330 | //-278, 331 | //-280, 332 | //-282, 333 | //-284, 334 | //-286, 335 | //-288, 336 | //-290, 337 | //-292, 338 | //-294, 339 | //-296, 340 | //-298, 341 | //-300, 342 | //-302, 343 | //-304, 344 | //-306, 345 | //-308, 346 | //-310, 347 | //-312, 348 | //-314, 349 | //-316, 350 | //-318, 351 | //-320, 352 | //-322, 353 | //-324, 354 | //-326, 355 | //-328, 356 | //-330, 357 | //-332, 358 | //-334, 359 | //-336, 360 | //-338, 361 | //-340, 362 | //-342, 363 | //-344, 364 | //-346, 365 | //-348, 366 | //-350, 367 | //-352, 368 | //-354, 369 | //-356, 370 | //-358, 371 | //-360, 372 | //-362, 373 | //-364, 374 | //-366, 375 | //-368, 376 | //-370, 377 | //-372, 378 | //-374, 379 | //-376, 380 | //-378, 381 | //-380, 382 | //-382, 383 | //-384, 384 | //-386, 385 | //-388, 386 | //-390, 387 | //-392, 388 | //-394, 389 | //-396, 390 | //-398, 391 | //-400, 392 | //-402, 393 | //-404, 394 | //-406, 395 | //-408, 396 | //-410, 397 | //-412, 398 | //-414, 399 | //-416, 400 | //-418, 401 | //-420, 402 | //-422, 403 | //-424 404 | //}; 405 | 406 | 407 | ////MB 408 | ////2019-05-25-1649-MiniStatButtonCellRevA 409 | //int16_t volts[num_array] = { 410 | //-16, 411 | //-18, 412 | //-20, 413 | //-22, 414 | //-24, 415 | //-26, 416 | //-28, 417 | //-30, 418 | //-32, 419 | //-34, 420 | //-36, 421 | //-38, 422 | //-40, 423 | //-42, 424 | //-44, 425 | //-46, 426 | //-48, 427 | //-50, 428 | //-52, 429 | //-54, 430 | //-56, 431 | //-58, 432 | //-60, 433 | //-62, 434 | //-64, 435 | //-66, 436 | //-68, 437 | //-70, 438 | //-72, 439 | //-74, 440 | //-76, 441 | //-78, 442 | //-80, 443 | //-82, 444 | //-84, 445 | //-86, 446 | //-88, 447 | //-90, 448 | //-92, 449 | //-94, 450 | //-96, 451 | //-98, 452 | //-100, 453 | //-102, 454 | //-104, 455 | //-106, 456 | //-108, 457 | //-110, 458 | //-112, 459 | //-114, 460 | //-116, 461 | //-118, 462 | //-120, 463 | //-122, 464 | //-124, 465 | //-126, 466 | //-128, 467 | //-130, 468 | //-132, 469 | //-134, 470 | //-136, 471 | //-138, 472 | //-140, 473 | //-142, 474 | //-144, 475 | //-146, 476 | //-148, 477 | //-150, 478 | //-152, 479 | //-154, 480 | //-156, 481 | //-158, 482 | //-160, 483 | //-162, 484 | //-164, 485 | //-166, 486 | //-168, 487 | //-170, 488 | //-172, 489 | //-174, 490 | //-176, 491 | //-178, 492 | //-180, 493 | //-182, 494 | //-184, 495 | //-186, 496 | //-188, 497 | //-190, 498 | //-192, 499 | //-194, 500 | //-196, 501 | //-198, 502 | //-200, 503 | //-202, 504 | //-204, 505 | //-206, 506 | //-208, 507 | //-210, 508 | //-212, 509 | //-214, 510 | //-216, 511 | //-218, 512 | //-220, 513 | //-222, 514 | //-224, 515 | //-226, 516 | //-228, 517 | //-230, 518 | //-232, 519 | //-234, 520 | //-236, 521 | //-238, 522 | //-240, 523 | //-242, 524 | //-244, 525 | //-246, 526 | //-248, 527 | //-250, 528 | //-252, 529 | //-254, 530 | //-256, 531 | //-258, 532 | //-260, 533 | //-262, 534 | //-264, 535 | //-266, 536 | //-268, 537 | //-270, 538 | //-272, 539 | //-274, 540 | //-276, 541 | //-278, 542 | //-280, 543 | //-282, 544 | //-284, 545 | //-286, 546 | //-288, 547 | //-290, 548 | //-292, 549 | //-294, 550 | //-296, 551 | //-298, 552 | //-300, 553 | //-302, 554 | //-304, 555 | //-306, 556 | //-308, 557 | //-310, 558 | //-312, 559 | //-314, 560 | //-316, 561 | //-318, 562 | //-320, 563 | //-322, 564 | //-324, 565 | //-326, 566 | //-328, 567 | //-330, 568 | //-332, 569 | //-334, 570 | //-336, 571 | //-338, 572 | //-340, 573 | //-342, 574 | //-344, 575 | //-346, 576 | //-348, 577 | //-350, 578 | //-352, 579 | //-354, 580 | //-356, 581 | //-358, 582 | //-360, 583 | //-362, 584 | //-364, 585 | //-366, 586 | //-368, 587 | //-370, 588 | //-372, 589 | //-374, 590 | //-376, 591 | //-378, 592 | //-380, 593 | //-382, 594 | //-384, 595 | //-386, 596 | //-388, 597 | //-390, 598 | //-392, 599 | //-394, 600 | //-396, 601 | //-398, 602 | //-400 603 | //}; 604 | // 605 | // 606 | ////MB 607 | ////2019-05-25-1649-MiniStatButtonCellRevA 608 | //float amps[num_array] = { 609 | //-23.58, 610 | //-32.81, 611 | //-35.01, 612 | //-38.7, 613 | //-39.06, 614 | //-42.01, 615 | //-42, 616 | //-41.99, 617 | //-41.24, 618 | //-41.5, 619 | //-42.79, 620 | //-41.86, 621 | //-42.78, 622 | //-41.48, 623 | //-43.51, 624 | //-41.84, 625 | //-43.5, 626 | //-42.94, 627 | //-46.07, 628 | //-47.36, 629 | //-46.43, 630 | //-44.4, 631 | //-45.68, 632 | //-44.76, 633 | //-45.3, 634 | //-45.85, 635 | //-43.72, 636 | //-46.58, 637 | //-43.53, 638 | //-45.65, 639 | //-45.18, 640 | //-46.57, 641 | //-46.84, 642 | //-45.64, 643 | //-44.81, 644 | //-45.45, 645 | //-47.57, 646 | //-46.74, 647 | //-47.01, 648 | //-46.18, 649 | //-47.56, 650 | //-46.73, 651 | //-48.85, 652 | //-48.39, 653 | //-47.92, 654 | //-48.2, 655 | //-47.73, 656 | //-50.22, 657 | //-49.02, 658 | //-50.77, 659 | //-48.46, 660 | //-50.95, 661 | //-49.38, 662 | //-51.13, 663 | //-51.78, 664 | //-52.05, 665 | //-50.48, 666 | //-51.49, 667 | //-51.4, 668 | //-50.64, 669 | //-51.11, 670 | //-50.34, 671 | //-52.3, 672 | //-49.45, 673 | //-50.89, 674 | //-50.63, 675 | //-52.95, 676 | //-52.18, 677 | //-53.02, 678 | //-52.39, 679 | //-53.1, 680 | //-53.57, 681 | //-52.57, 682 | //-54.76, 683 | //-53.38, 684 | //-53.49, 685 | //-53.83, 686 | //-54.67, 687 | //-55.15, 688 | //-56.22, 689 | //-57.07, 690 | //-57.18, 691 | //-54.56, 692 | //-56.14, 693 | //-59.21, 694 | //-55.84, 695 | //-57.43, 696 | //-57.17, 697 | //-56.76, 698 | //-57.24, 699 | //-58.46, 700 | //-57.31, 701 | //-58.46, 702 | //-58.97, 703 | //-58.37, 704 | //-57.76, 705 | //-59.75, 706 | //-59.89, 707 | //-58.92, 708 | //-61.64, 709 | //-59.56, 710 | //-62.29, 711 | //-61.31, 712 | //-63.3, 713 | //-60.11, 714 | //-63.94, 715 | //-63.71, 716 | //-62.74, 717 | //-63.25, 718 | //-62.64, 719 | //-64.63, 720 | //-64.03, 721 | //-66.75, 722 | //-65.04, 723 | //-67.03, 724 | //-64.95, 725 | //-68.04, 726 | //-67.07, 727 | //-70.53, 728 | //-69.19, 729 | //-68.59, 730 | //-70.21, 731 | //-70.34, 732 | //-72.33, 733 | //-70.62, 734 | //-73.71, 735 | //-71.65, 736 | //-75.45, 737 | //-74.09, 738 | //-74.57, 739 | //-73.94, 740 | //-78.11, 741 | //-77.48, 742 | //-77.22, 743 | //-81.77, 744 | //-79.66, 745 | //-77.18, 746 | //-81.73, 747 | //-83.32, 748 | //-82.32, 749 | //-84.65, 750 | //-83.28, 751 | //-83.76, 752 | //-87.93, 753 | //-86.19, 754 | //-88.89, 755 | //-88.63, 756 | //-90.22, 757 | //-87.74, 758 | //-90.07, 759 | //-90.92, 760 | //-90.29, 761 | //-88.92, 762 | //-91.25, 763 | //-93.21, 764 | //-91.84, 765 | //-94.17, 766 | //-94.28, 767 | //-93.28, 768 | //-93.58, 769 | //-94.86, 770 | //-92.95, 771 | //-95.33, 772 | //-96.98, 773 | //-97.66, 774 | //-96.35, 775 | //-96.14, 776 | //-97.56, 777 | //-97.36, 778 | //-98.64, 779 | //-97.84, 780 | //-101.7, 781 | //-99.65, 782 | //-100.7, 783 | //-101.61, 784 | //-101.4, 785 | //-102.08, 786 | //-104.1, 787 | //-106.11, 788 | //-106.79, 789 | //-108.81, 790 | //-110.82, 791 | //-110.02, 792 | //-109.82, 793 | //-112.95, 794 | //-112.89, 795 | //-116.75, 796 | //-116.18, 797 | //-119.08, 798 | //-121.09, 799 | //-123.85, 800 | //-123.42, 801 | //-129.99 802 | //}; 803 | 804 | 805 | //MB 806 | //2019-05-24-1555-MiniStatButtonCellRevA 807 | int16_t volts[] = { 808 | -18, 809 | -20, 810 | -22, 811 | -24, 812 | -26, 813 | -28, 814 | -30, 815 | -32, 816 | -34, 817 | -36, 818 | -38, 819 | -40, 820 | -42, 821 | -44, 822 | -46, 823 | -48, 824 | -50, 825 | -52, 826 | -54, 827 | -56, 828 | -58, 829 | -60, 830 | -62, 831 | -64, 832 | -66, 833 | -68, 834 | -70, 835 | -72, 836 | -74, 837 | -76, 838 | -78, 839 | -80, 840 | -82, 841 | -84, 842 | -86, 843 | -88, 844 | -90, 845 | -92, 846 | -94, 847 | -96, 848 | -98, 849 | -100, 850 | -102, 851 | -104, 852 | -106, 853 | -108, 854 | -110, 855 | -112, 856 | -114, 857 | -116, 858 | -118, 859 | -120, 860 | -122, 861 | -124, 862 | -126, 863 | -128, 864 | -130, 865 | -132, 866 | -134, 867 | -136, 868 | -138, 869 | -140, 870 | -142, 871 | -144, 872 | -146, 873 | -148, 874 | -150, 875 | -152, 876 | -154, 877 | -156, 878 | -158, 879 | -160, 880 | -162, 881 | -164, 882 | -166, 883 | -168, 884 | -170, 885 | -172, 886 | -174, 887 | -176, 888 | -178, 889 | -180, 890 | -182, 891 | -184, 892 | -186, 893 | -188, 894 | -190, 895 | -192, 896 | -194, 897 | -196, 898 | -198, 899 | -200, 900 | -202, 901 | -204, 902 | -206, 903 | -208, 904 | -210, 905 | -212, 906 | -214, 907 | -216, 908 | -218, 909 | -220, 910 | -222, 911 | -224, 912 | -226, 913 | -228, 914 | -230, 915 | -232, 916 | -234, 917 | -236, 918 | -238, 919 | -240, 920 | -242, 921 | -244, 922 | -246, 923 | -248, 924 | -250, 925 | -252, 926 | -254, 927 | -256, 928 | -258, 929 | -260, 930 | -262, 931 | -264, 932 | -266, 933 | -268, 934 | -270, 935 | -272, 936 | -274, 937 | -276, 938 | -278, 939 | -280, 940 | -282, 941 | -284, 942 | -286, 943 | -288, 944 | -290, 945 | -292, 946 | -294, 947 | -296, 948 | -298, 949 | -300, 950 | -302, 951 | -304, 952 | -306, 953 | -308, 954 | -310, 955 | -312, 956 | -314, 957 | -316, 958 | -318, 959 | -320, 960 | -322, 961 | -324, 962 | -326, 963 | -328, 964 | -330, 965 | -332, 966 | -334, 967 | -336, 968 | -338, 969 | -340, 970 | -342, 971 | -344, 972 | -346, 973 | -348, 974 | -350, 975 | -352, 976 | -354, 977 | -356, 978 | -358, 979 | -360, 980 | -362, 981 | -364, 982 | -366, 983 | -368, 984 | -370, 985 | -372, 986 | -374, 987 | -376, 988 | -378, 989 | -380, 990 | -382, 991 | -384, 992 | -386, 993 | -388, 994 | -390, 995 | -392, 996 | -394, 997 | -396, 998 | -398, 999 | -400, 1000 | -402, 1001 | -404, 1002 | -406, 1003 | -408, 1004 | -410, 1005 | -412, 1006 | -414, 1007 | -416, 1008 | -418, 1009 | -420, 1010 | -422, 1011 | -424, 1012 | -426, 1013 | -428, 1014 | -430, 1015 | -432, 1016 | -434, 1017 | -436, 1018 | -438, 1019 | -440, 1020 | -442, 1021 | -444, 1022 | -446, 1023 | -448, 1024 | -450 1025 | }; 1026 | 1027 | 1028 | //MB 1029 | //2019-05-24-1555-MiniStatButtonCellRevA 1030 | float amps[] = { 1031 | -12.11, 1032 | -15.42, 1033 | -17.26, 1034 | -19.47, 1035 | -20.94, 1036 | -19.08, 1037 | -22.4, 1038 | -20.91, 1039 | -21.17, 1040 | -20.24, 1041 | -22.64, 1042 | -21.34, 1043 | -24.85, 1044 | -22.07, 1045 | -24.84, 1046 | -23.54, 1047 | -24.83, 1048 | -26.48, 1049 | -25.55, 1050 | -26.84, 1051 | -25.54, 1052 | -27.94, 1053 | -27.75, 1054 | -29.41, 1055 | -28.48, 1056 | -28.19, 1057 | -27.36, 1058 | -26.15, 1059 | -27.54, 1060 | -28.92, 1061 | -29.94, 1062 | -30.21, 1063 | -30.85, 1064 | -30.76, 1065 | -31.03, 1066 | -31.68, 1067 | -32.32, 1068 | -32.6, 1069 | -32.87, 1070 | -32.41, 1071 | -32.31, 1072 | -32.96, 1073 | -33.23, 1074 | -34.99, 1075 | -33.04, 1076 | -35.53, 1077 | -34.33, 1078 | -35.34, 1079 | -33.03, 1080 | -37, 1081 | -35.8, 1082 | -36.81, 1083 | -37.09, 1084 | -38.1, 1085 | -37.64, 1086 | -36.43, 1087 | -38.56, 1088 | -37.35, 1089 | -38.81, 1090 | -37.44, 1091 | -39.25, 1092 | -38.25, 1093 | -38.36, 1094 | -39.8, 1095 | -39.91, 1096 | -39.28, 1097 | -39.98, 1098 | -41.94, 1099 | -40.93, 1100 | -42.01, 1101 | -40.64, 1102 | -42.22, 1103 | -41.82, 1104 | -42.3, 1105 | -42.03, 1106 | -43.11, 1107 | -43.58, 1108 | -42.95, 1109 | -44.03, 1110 | -42.65, 1111 | -44.61, 1112 | -44.95, 1113 | -44.68, 1114 | -43.31, 1115 | -45.86, 1116 | -46.34, 1117 | -46.82, 1118 | -46.04, 1119 | -46.52, 1120 | -46.26, 1121 | -47.33, 1122 | -46.63, 1123 | -47.51, 1124 | -49.13, 1125 | -48.15, 1126 | -49.4, 1127 | -48.06, 1128 | -49.31, 1129 | -49.44, 1130 | -49.95, 1131 | -49.72, 1132 | -49.86, 1133 | -51.84, 1134 | -49.02, 1135 | -52.49, 1136 | -50.03, 1137 | -51.65, 1138 | -51.79, 1139 | -53.77, 1140 | -50.95, 1141 | -68.09, 1142 | -53.08, 1143 | -53.95, 1144 | -54.46, 1145 | -54.6, 1146 | -54.37, 1147 | -54.87, 1148 | -56.86, 1149 | -57.37, 1150 | -56.76, 1151 | -56.53, 1152 | -57.04, 1153 | -56.44, 1154 | -57.68, 1155 | -57.82, 1156 | -57.97, 1157 | -59.19, 1158 | -59.3, 1159 | -61.63, 1160 | -60.26, 1161 | -61.11, 1162 | -61.22, 1163 | -62.44, 1164 | -64.77, 1165 | -63.77, 1166 | -63.14, 1167 | -64.36, 1168 | -65.94, 1169 | -66.05, 1170 | -68.01, 1171 | -67.01, 1172 | -68.97, 1173 | -67.23, 1174 | -70.3, 1175 | -69.67, 1176 | -73.11, 1177 | -73.96, 1178 | -74.81, 1179 | -74.18, 1180 | -75.76, 1181 | -78.09, 1182 | -77.46, 1183 | -85.33, 1184 | -81.01, 1185 | -81.49, 1186 | -80.49, 1187 | -83.19, 1188 | -81.82, 1189 | -83.6, 1190 | -84.14, 1191 | -85.92, 1192 | -86.09, 1193 | -88.11, 1194 | -87.31, 1195 | -87.11, 1196 | -87.27, 1197 | -86.47, 1198 | -89.97, 1199 | -88.29, 1200 | -88.97, 1201 | -92.09, 1202 | -87.45, 1203 | -87.39, 1204 | -89.41, 1205 | -87.73, 1206 | -82.86, 1207 | -88.94, 1208 | -88.74, 1209 | -88.31, 1210 | -87.74, 1211 | -88.65, 1212 | -88.22, 1213 | -88.01, 1214 | -85.22, 1215 | -87.75, 1216 | -86.44, 1217 | -89.57, 1218 | -89.14, 1219 | -87.82, 1220 | -90.21, 1221 | -87.56, 1222 | -91.55, 1223 | -89.55, 1224 | -91.26, 1225 | -91.48, 1226 | -92.3, 1227 | -93.64, 1228 | -92.75, 1229 | -94.68, 1230 | -93.06, 1231 | -94.02, 1232 | -94.25, 1233 | -98.03, 1234 | -96.03, 1235 | -99.96, 1236 | -98.19, 1237 | -101.37, 1238 | -99.01, 1239 | -101.82, 1240 | -103.01, 1241 | -102.87, 1242 | -103.83, 1243 | -105.76, 1244 | -107.47, 1245 | -106.95, 1246 | -108.28, 1247 | -107.63 1248 | }; 1249 | 1250 | 1251 | ////FeCN 1252 | ////2019-04-09-1741-MiniStatButtonCellRevA 1253 | //int16_t volts[] = { 1254 | //450, 1255 | //445, 1256 | //440, 1257 | //435, 1258 | //430, 1259 | //425, 1260 | //420, 1261 | //415, 1262 | //410, 1263 | //405, 1264 | //400, 1265 | //395, 1266 | //390, 1267 | //385, 1268 | //380, 1269 | //375, 1270 | //370, 1271 | //365, 1272 | //360, 1273 | //355, 1274 | //350, 1275 | //345, 1276 | //340, 1277 | //335, 1278 | //330, 1279 | //325, 1280 | //320, 1281 | //315, 1282 | //310, 1283 | //305, 1284 | //300, 1285 | //295, 1286 | //290, 1287 | //285, 1288 | //280, 1289 | //275, 1290 | //270, 1291 | //265, 1292 | //260, 1293 | //255, 1294 | //250, 1295 | //245, 1296 | //240, 1297 | //235, 1298 | //230, 1299 | //225, 1300 | //220, 1301 | //215, 1302 | //210, 1303 | //205, 1304 | //200, 1305 | //195, 1306 | //190, 1307 | //185, 1308 | //180, 1309 | //175, 1310 | //170, 1311 | //165, 1312 | //160, 1313 | //155, 1314 | //150, 1315 | //145, 1316 | //140, 1317 | //135, 1318 | //130, 1319 | //125, 1320 | //120, 1321 | //115, 1322 | //110, 1323 | //105, 1324 | //100, 1325 | //95, 1326 | //90, 1327 | //85, 1328 | //80, 1329 | //75, 1330 | //70, 1331 | //65, 1332 | //60, 1333 | //55, 1334 | //50, 1335 | //45, 1336 | //40, 1337 | //35, 1338 | //30, 1339 | //25, 1340 | //20, 1341 | //15, 1342 | //10, 1343 | //5, 1344 | //0, 1345 | //-5, 1346 | //-10, 1347 | //-15, 1348 | //-20, 1349 | //-25, 1350 | //-30, 1351 | //-35, 1352 | //-40, 1353 | //-45, 1354 | //-50, 1355 | //-55, 1356 | //-60, 1357 | //-65, 1358 | //-70, 1359 | //-75, 1360 | //-80, 1361 | //-85, 1362 | //-90, 1363 | //-95, 1364 | //-100, 1365 | //-105, 1366 | //-110, 1367 | //-115, 1368 | //-120, 1369 | //-125, 1370 | //-130, 1371 | //-135, 1372 | //-140, 1373 | //-145, 1374 | //-150, 1375 | //-155, 1376 | //-160, 1377 | //-165, 1378 | //-170, 1379 | //-175, 1380 | //-180, 1381 | //-185, 1382 | //-190, 1383 | //-195, 1384 | //-200, 1385 | //-195, 1386 | //-190, 1387 | //-185, 1388 | //-180, 1389 | //-175, 1390 | //-170, 1391 | //-165, 1392 | //-160, 1393 | //-155, 1394 | //-150, 1395 | //-145, 1396 | //-140, 1397 | //-135, 1398 | //-130, 1399 | //-125, 1400 | //-120, 1401 | //-115, 1402 | //-110, 1403 | //-105, 1404 | //-100, 1405 | //-95, 1406 | //-90, 1407 | //-85, 1408 | //-80, 1409 | //-75, 1410 | //-70, 1411 | //-65, 1412 | //-60, 1413 | //-55, 1414 | //-50, 1415 | //-45, 1416 | //-40, 1417 | //-35, 1418 | //-30, 1419 | //-25, 1420 | //-20, 1421 | //-15, 1422 | //-10, 1423 | //-5, 1424 | //0, 1425 | //-5, 1426 | //0, 1427 | //5, 1428 | //10, 1429 | //15, 1430 | //20, 1431 | //25, 1432 | //30, 1433 | //35, 1434 | //40, 1435 | //45, 1436 | //50, 1437 | //55, 1438 | //60, 1439 | //65, 1440 | //70, 1441 | //75, 1442 | //80, 1443 | //85, 1444 | //90, 1445 | //95, 1446 | //100, 1447 | //105, 1448 | //110, 1449 | //115, 1450 | //120, 1451 | //125, 1452 | //130, 1453 | //135, 1454 | //140, 1455 | //145, 1456 | //150, 1457 | //155, 1458 | //160, 1459 | //165, 1460 | //170, 1461 | //175, 1462 | //180, 1463 | //185, 1464 | //190, 1465 | //195, 1466 | //200, 1467 | //205, 1468 | //210, 1469 | //215, 1470 | //220, 1471 | //225, 1472 | //230, 1473 | //235, 1474 | //240, 1475 | //245, 1476 | //250, 1477 | //255, 1478 | //260, 1479 | //265, 1480 | //270, 1481 | //275, 1482 | //280, 1483 | //285, 1484 | //290, 1485 | //295, 1486 | //300, 1487 | //305, 1488 | //310, 1489 | //315, 1490 | //320, 1491 | //325, 1492 | //330, 1493 | //335, 1494 | //340, 1495 | //345, 1496 | //350, 1497 | //355, 1498 | //360, 1499 | //365, 1500 | //370, 1501 | //375, 1502 | //380, 1503 | //385, 1504 | //390, 1505 | //395, 1506 | //400, 1507 | //405, 1508 | //410, 1509 | //415, 1510 | //420, 1511 | //425, 1512 | //430, 1513 | //435, 1514 | //440, 1515 | //445 1516 | //}; 1517 | // 1518 | // 1519 | ////FeCN 1520 | ////2019-04-09-1741-MiniStatButtonCellRevA 1521 | //float amps[] = { 1522 | //3.126428571, 1523 | //2.902142857, 1524 | //2.561428571, 1525 | //2.475, 1526 | //2.367142857, 1527 | //2.258571429, 1528 | //2.15, 1529 | //2.296428571, 1530 | //2.188571429, 1531 | //2.08, 1532 | //2.052142857, 1533 | //1.828571429, 1534 | //1.840714286, 1535 | //1.772142857, 1536 | //1.704285714, 1537 | //1.948571429, 1538 | //1.647857143, 1539 | //1.637857143, 1540 | //1.766428571, 1541 | //1.814285714, 1542 | //1.92, 1543 | //1.641428571, 1544 | //1.457142857, 1545 | //1.795714286, 1546 | //1.565, 1547 | //1.492142857, 1548 | //1.302857143, 1549 | //1.520714286, 1550 | //1.331428571, 1551 | //1.2, 1552 | //1.301428571, 1553 | //1.17, 1554 | //1.271428571, 1555 | //1.082142857, 1556 | //1.125714286, 1557 | //1.227142857, 1558 | //1.27, 1559 | //0.904285714, 1560 | //1.201428571, 1561 | //0.663571429, 1562 | //0.554285714, 1563 | //0.713571429, 1564 | //0.372142857, 1565 | //0.124285714, 1566 | //-0.332857143, 1567 | //-0.290714286, 1568 | //-0.573571429, 1569 | //-1.053571429, 1570 | //-1.220714286, 1571 | //-2.049285714, 1572 | //-2.565, 1573 | //-2.806428571, 1574 | //-3.581428571, 1575 | //-4.218571429, 1576 | //-5.087857143, 1577 | //-6.095714286, 1578 | //-7.022857143, 1579 | //-7.950714286, 1580 | //-9.190714286, 1581 | //-10.06, 1582 | //-11.39428571, 1583 | //-12.51785714, 1584 | //-13.38714286, 1585 | //-14.43071429, 1586 | //-15.41071429, 1587 | //-16.46428571, 1588 | //-16.87928571, 1589 | //-17.58428571, 1590 | //-18.28928571, 1591 | //-18.29785714, 1592 | //-18.88714286, 1593 | //-18.77928571, 1594 | //-18.96142857, 1595 | //-19.08571429, 1596 | //-18.92, 1597 | //-18.46357143, 1598 | //-18.18142857, 1599 | //-18.02428571, 1600 | //-17.86642857, 1601 | //-17.47642857, 1602 | //-17.14428571, 1603 | //-16.69642857, 1604 | //-16.59642857, 1605 | //-15.8, 1606 | //-15.65928571, 1607 | //-15.34357143, 1608 | //-14.91214286, 1609 | //-14.53857143, 1610 | //-13.84142857, 1611 | //-13.435, 1612 | //-13.84142857, 1613 | //-13.78357143, 1614 | //-13.20285714, 1615 | //-12.68, 1616 | //-12.93714286, 1617 | //-12.38142857, 1618 | //-12.52285714, 1619 | //-12.08214286, 1620 | //-12.00785714, 1621 | //-12.04928571, 1622 | //-11.56857143, 1623 | //-11.49357143, 1624 | //-11.47714286, 1625 | //-11.34428571, 1626 | //-10.68857143, 1627 | //-10.85428571, 1628 | //-10.73, 1629 | //-10.43142857, 1630 | //-10.365, 1631 | //-10.58928571, 1632 | //-10.05785714, 1633 | //-10.22428571, 1634 | //-10.21571429, 1635 | //-10.38214286, 1636 | //-9.909285714, 1637 | //-9.668571429, 1638 | //-9.485714286, 1639 | //-9.61, 1640 | //-9.553571429, 1641 | //-9.555714286, 1642 | //-9.302857143, 1643 | //-9.247142857, 1644 | //-9.307142857, 1645 | //-9.286428571, 1646 | //-9.172142857, 1647 | //-8.942142857, 1648 | //-9.037857143, 1649 | //-8.923571429, 1650 | //-8.867142857, 1651 | //-8.788571429, 1652 | //-8.722142857, 1653 | //-8.498571429, 1654 | //-8.460714286, 1655 | //-8.400714286, 1656 | //-8.108571429, 1657 | //-7.78, 1658 | //-8.068571429, 1659 | //-8.008571429, 1660 | //-7.912857143, 1661 | //-7.736428571, 1662 | //-7.502142857, 1663 | //-7.406428571, 1664 | //-7.346428571, 1665 | //-7.577142857, 1666 | //-7.162142857, 1667 | //-7.402857143, 1668 | //-7.411428571, 1669 | //-7.129285714, 1670 | //-7.079285714, 1671 | //-7.029285714, 1672 | //-7.037857143, 1673 | //-6.697142857, 1674 | //-7.112142857, 1675 | //-6.83, 1676 | //-6.954285714, 1677 | //-6.788571429, 1678 | //-6.564285714, 1679 | //-6.64, 1680 | //-6.482142857, 1681 | //-6.556428571, 1682 | //-6.572857143, 1683 | //-6.473571429, 1684 | //-6.547857143, 1685 | //-6.157857143, 1686 | //-6.307857143, 1687 | //-6.108571429, 1688 | //-6.025, 1689 | //-5.651428571, 1690 | //-5.825714286, 1691 | //-5.651428571, 1692 | //-5.477142857, 1693 | //-5.825714286, 1694 | //-5.535, 1695 | //-5.128571429, 1696 | //-5.128571429, 1697 | //-5.419285714, 1698 | //-5.27, 1699 | //-5.237142857, 1700 | //-4.971428571, 1701 | //-4.589285714, 1702 | //-4.805, 1703 | //-4.672857143, 1704 | //-4.249285714, 1705 | //-4.117142857, 1706 | //-3.81, 1707 | //-3.561428571, 1708 | //-3.021428571, 1709 | //-2.722857143, 1710 | //-2.249285714, 1711 | //-1.718571429, 1712 | //-1.187857143, 1713 | //-0.307857143, 1714 | //-0.009285714, 1715 | //0.986428571, 1716 | //1.633571429, 1717 | //2.512857143, 1718 | //3.625, 1719 | //4.33, 1720 | //5.442142857, 1721 | //6.479285714, 1722 | //7.581428571, 1723 | //8.566428571, 1724 | //9.458571429, 1725 | //10.73428571, 1726 | //11.54571429, 1727 | //12.43714286, 1728 | //13.13214286, 1729 | //13.76928571, 1730 | //13.96357143, 1731 | //14.65928571, 1732 | //14.88928571, 1733 | //15.02571429, 1734 | //15.03428571, 1735 | //14.96928571, 1736 | //15.04285714, 1737 | //14.80357143, 1738 | //14.35428571, 1739 | //14.17285714, 1740 | //13.54928571, 1741 | //13.19357143, 1742 | //12.86, 1743 | //12.44642857, 1744 | //12.055, 1745 | //11.7, 1746 | //11.30857143, 1747 | //11.06928571, 1748 | //10.73785714, 1749 | //10.52071429, 1750 | //10.245, 1751 | //10.02714286, 1752 | //9.693571429, 1753 | //9.07, 1754 | //9.026428571, 1755 | //8.867142857, 1756 | //8.882142857, 1757 | //8.374285714, 1758 | //8.215, 1759 | //7.997857143, 1760 | //8.012857143, 1761 | //7.72, 1762 | //7.382142857, 1763 | //7.45, 1764 | //7.147857143, 1765 | //7.157857143, 1766 | //6.993571429, 1767 | //6.807142857, 1768 | //6.759285714, 1769 | //6.362857143, 1770 | //6.525, 1771 | //6.245, 1772 | //6.312857143, 1773 | //5.894285714, 1774 | //6.117857143, 1775 | //6.03, 1776 | //6.08, 1777 | //5.897857143, 1778 | //5.751428571, 1779 | //5.86, 1780 | //5.561428571, 1781 | //5.321428571, 1782 | //5.232857143, 1783 | //5.457857143 1784 | //}; 1785 | 1786 | 1787 | unsigned long time1 = 0; 1788 | unsigned long time2 = 0; 1789 | unsigned long time3 = 0; 1790 | unsigned long time4 = 0; 1791 | unsigned long dt = 0; 1792 | 1793 | 1794 | float slope = 0; 1795 | float intercept = 0; 1796 | float current = 0; 1797 | 1798 | 1799 | void setup() 1800 | { 1801 | SerialDebugger.begin(115200); 1802 | while(!SerialDebugger); 1803 | 1804 | 1805 | time1 = millis(); 1806 | uint16_t errorCode = analyst.calcBaseline(MB_APTAMER_BASE, amps, volts, slope, intercept, num_array); 1807 | //uint16_t errorCode = analyst.calcBaseline(FeCN_RED_BASE, amps, volts, slope, intercept, num_array); 1808 | time2 = millis(); 1809 | dt = time2-time1; 1810 | SerialDebugger.print("dt = "); 1811 | SerialDebugger.println(dt); 1812 | printErrorCode(errorCode); 1813 | printSlopeAndIntercept(); 1814 | 1815 | 1816 | time1 = millis(); 1817 | current = analyst.getPeakCurrent(MB_APTAMER_PEAK, amps, volts, slope, intercept, num_array); 1818 | //current = analyst.getPeakCurrent(FeCN_RED_PEAK, amps, volts, slope, intercept, num_array); 1819 | time2 = millis(); 1820 | dt = time2-time1; 1821 | SerialDebugger.print("dt = "); 1822 | SerialDebugger.println(dt); 1823 | printCurrent(); 1824 | 1825 | 1826 | // time1 = millis(); 1827 | // errorCode = analyst.calcBaseline(FeCN_OX_BASE, amps, volts, slope, intercept, num_array); 1828 | // time2 = millis(); 1829 | // dt = time2-time1; 1830 | // SerialDebugger.print("dt = "); 1831 | // SerialDebugger.println(dt); 1832 | // printErrorCode(errorCode); 1833 | // printSlopeAndIntercept(); 1834 | // 1835 | // time1 = millis(); 1836 | // current = analyst.getPeakCurrent(FeCN_OX_PEAK, amps, volts, slope, intercept, num_array); 1837 | // time2 = millis(); 1838 | // dt = time2-time1; 1839 | // SerialDebugger.print("dt = "); 1840 | // SerialDebugger.println(dt); 1841 | // printCurrent(); 1842 | 1843 | } 1844 | 1845 | void printErrorCode(uint16_t code) 1846 | { 1847 | SerialDebugger.print("Error code: "); 1848 | SerialDebugger.println(code); 1849 | } 1850 | 1851 | void printSlopeAndIntercept() 1852 | { 1853 | SerialDebugger.print("slope: "); 1854 | SerialDebugger.print(slope,4); 1855 | SerialDebugger.print(", "); 1856 | SerialDebugger.print("intercept: "); 1857 | SerialDebugger.print(intercept,4); 1858 | SerialDebugger.println(); 1859 | } 1860 | 1861 | void printCurrent() 1862 | { 1863 | SerialDebugger.print("current: "); 1864 | SerialDebugger.print(current); 1865 | SerialDebugger.println(); 1866 | } 1867 | 1868 | void loop() 1869 | { 1870 | } 1871 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/NormalPulseVoltammetry/NormalPulseVoltammetry.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | LMP91000 pStat = LMP91000(); 5 | 6 | int16_t opVolt = 3300; //milliVolts if working with a 3.3V device 7 | uint8_t resolution = 10; //10-bits 8 | 9 | void setup() 10 | { 11 | Wire.begin(); 12 | Serial.begin(115200); 13 | 14 | pStat.standby(); 15 | delay(1000); //warm-up time for the gas sensor 16 | 17 | runNPV(1,200,50,0,500,4000,6); 18 | } 19 | 20 | 21 | void loop() 22 | { 23 | } 24 | 25 | 26 | void runNPV(uint8_t user_gain, uint32_t pulse_period, uint32_t pulse_width, 27 | int16_t startV, int16_t endV, uint32_t quietTime, uint8_t range) 28 | { 29 | pStat.disableFET(); 30 | pStat.setGain(user_gain); 31 | pStat.setRLoad(0); 32 | pStat.setIntRefSource(); 33 | pStat.setIntZ(1); 34 | pStat.setThreeLead(); 35 | pStat.setBias(0); 36 | pStat.setPosBias(); 37 | delay(quietTime); 38 | 39 | //Print column headers 40 | String current = ""; 41 | if(range == 12) current = "Current(pA)"; 42 | else if(range == 9) current = "Current(nA)"; 43 | else if(range == 6) current = "Current(uA)"; 44 | else if(range == 3) current = "Current(mA)"; 45 | else current = "SOME ERROR"; 46 | 47 | Serial.println("Voltage(mV)," + current); 48 | 49 | uint32_t off_time = pulse_period - pulse_width; 50 | 51 | startV = determineLMP91000Bias(startV); 52 | startV = opVolt*TIA_BIAS[abs(startV)]*startV/abs(startV); 53 | endV = determineLMP91000Bias(endV); 54 | endV = opVolt*TIA_BIAS[abs(endV)]*endV/abs(endV); 55 | 56 | 57 | //this divides the voltage span for the scan 58 | //by voltage resolution of the LMP91000 59 | //referenced to the chip's operating voltage 60 | uint8_t pulses = abs((endV-startV)/(opVolt*0.02)); 61 | int16_t incrV = (endV-startV)/pulses; 62 | 63 | startV = determineLMP91000Bias(startV); 64 | unsigned long startTime = 0; 65 | signed char sign = 0; 66 | int16_t nextV = 0; 67 | 68 | for (uint8_t i = 0; i < pulses; i++) 69 | { 70 | //baseline voltage 71 | startTime = millis(); 72 | if(startV < 0) sign = 0; 73 | else sign = 1; 74 | pStat.setBias(abs(startV), sign); 75 | while(millis() - startTime < off_time); 76 | 77 | 78 | //find next voltage potential 79 | if(startV != 0) nextV = (opVolt*TIA_BIAS[abs(startV)]*startV/abs(startV)) + i*incrV; 80 | else nextV += i*incrV; 81 | nextV = determineLMP91000Bias(nextV); 82 | 83 | 84 | //start next pulse 85 | startTime = millis(); 86 | if(nextV < 0) sign = 0; 87 | else sign = 1; 88 | pStat.setBias(abs(nextV), sign); 89 | while(millis() - startTime < pulse_width); 90 | 91 | 92 | //print output current 93 | Serial.print((uint16_t)(opVolt*TIA_BIAS[abs(nextV)]*(nextV/abs(nextV)))); 94 | Serial.print(","); 95 | Serial.println(pow(10,range)*pStat.getCurrent(pStat.getOutput(A0), opVolt/1000.0, resolution)); 96 | } 97 | 98 | //end at 0V 99 | pStat.setBias(0); 100 | } 101 | 102 | 103 | signed char determineLMP91000Bias(int16_t voltage) 104 | { 105 | signed char polarity = 0; 106 | if(voltage < 0) polarity = -1; 107 | else polarity = 1; 108 | 109 | int16_t v1 = 0; 110 | int16_t v2 = 0; 111 | 112 | voltage = abs(voltage); 113 | 114 | if(voltage == 0) return 0; 115 | 116 | for(int i = 0; i < NUM_TIA_BIAS-1; i++) 117 | { 118 | v1 = opVolt*TIA_BIAS[i]; 119 | v2 = opVolt*TIA_BIAS[i+1]; 120 | 121 | if(voltage == v1) return polarity*i; 122 | else if(voltage > v1 && voltage < v2) 123 | { 124 | if(abs(voltage-v1) < abs(voltage-v2)) return polarity*i; 125 | else return polarity*(i+1); 126 | } 127 | } 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/SquareWaveVoltammetry-wAD5644R-for-Aptamer/SquareWaveVoltammetry-wAD5644R-for-Aptamer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * FILENAME: SquareWaveVoltammetry-wDueDAC.ino 3 | * AUTHOR: Orlando Hoilett 4 | * VERSION: V.1.0.0 5 | * 6 | * 7 | * DESCRIPTION 8 | * 9 | * 10 | * 11 | * UPDATES 12 | * 13 | * 14 | * 15 | * DISCLAIMER 16 | * Linnes Lab code, firmware, and software is released under the 17 | * MIT License (http://opensource.org/licenses/MIT). 18 | * 19 | * The MIT License (MIT) 20 | * 21 | * Copyright (c) 2019 Linnes Lab, Purdue University 22 | * 23 | * Permission is hereby granted, free of charge, to any person 24 | * obtaining a copy of this software and associated documentation 25 | * files (the "Software"), to deal in the Software without 26 | * restriction, including without limitation the rights to use, 27 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | * copies of the Software, and to permit persons to whom the Software 29 | * is furnished to do so, subject to the following conditions: 30 | * 31 | * The above copyright notice and this permission notice shall be included 32 | * in all copies or substantial portions of the Software. 33 | * 34 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 35 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 37 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 39 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 40 | * IN THE SOFTWARE. 41 | * 42 | * 43 | */ 44 | 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include ; 51 | #include "pgmStrToRAM.h"; //SerialUSB.println(freeMemory(), DEC); // print how much RAM is available. 52 | 53 | 54 | LMP91000 pStat = LMP91000(); 55 | 56 | const int AD56X4_SS_pin = 10; 57 | 58 | const uint16_t opVolt = 3280; 59 | const uint8_t adcBits = 10; 60 | const double v_tolerance = 0.009; 61 | const uint8_t currentRange = 9; 62 | const double extGain = 180000; 63 | 64 | 65 | //const uint16_t dacMin = 0; //0V 66 | //const uint16_t dacMax = opVolt; //Vdd 67 | //const uint16_t dacSpan = opVolt; //Vdd 68 | const uint16_t dacResolution = pow(2,16)-1; //16-bit 69 | 70 | 71 | bool debug = false; 72 | 73 | 74 | //analog input pins to read voltages 75 | const uint8_t LMP = A2; 76 | const uint8_t C1 = A0; 77 | const uint8_t C2 = A1; 78 | const uint8_t DACRead = A3; 79 | const uint8_t diffAmp = A6; 80 | const uint8_t INA = A7; 81 | 82 | 83 | void setup() 84 | { 85 | Wire.begin(); 86 | Serial.begin(115200); 87 | 88 | 89 | pinMode(AD56X4_SS_pin,OUTPUT); 90 | SPI.setClockDivider(SPI_CLOCK_DIV2); 91 | SPI.begin(); 92 | 93 | AD56X4.reset(AD56X4_SS_pin,true); 94 | 95 | 96 | //enable the potentiostat 97 | delay(50); 98 | pStat.standby(); 99 | delay(50); 100 | initLMP(5); 101 | delay(2000); //warm-up time for the gas sensor 102 | 103 | } 104 | 105 | 106 | void loop() 107 | { 108 | Serial.println(F("Ready!")); 109 | 110 | //will hold the code here until a character is sent over the serial port 111 | //this ensures the experiment will only run when initiated 112 | while(!Serial.available()); 113 | Serial.read(); 114 | 115 | 116 | //prints column headings 117 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 118 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,LMP,C1,C2"); 119 | 120 | // //prints column headings 121 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 122 | // else Serial.println("Time(ms),Volt,diffAmp,INA,Time(ms),Volt,diffAmp,INA"); 123 | 124 | 125 | // //prints column headings 126 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 127 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,Time(ms),Volt,LMP,C1,C2"); 128 | 129 | 130 | //Serial.println("Time(ms),Volt,C1,C2,INA,Time(ms),Volt,C1,C2,INA"); 131 | Serial.println("Time(ms),Volt,C1,C2,Diff,INA,Time(ms),Volt,C1,C2,Diff,INA"); 132 | 133 | 134 | // //prints column headings 135 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 136 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,Diff,INA,Time(ms),Volt,LMP,C1,C2,Diff,INA"); 137 | 138 | 139 | //lmpGain, startV(mV), endV(mV), pulseAmp(mV), stepV(mV), freq(Hz) 140 | runSWV(0, 50, -500, 50, 5, 120); 141 | Serial.println("Backward scan"); 142 | runSWV(0, -500, 50, 50, 5, 120); 143 | setOutputsToZero(); 144 | } 145 | 146 | 147 | void initLMP(uint8_t lmpGain) 148 | { 149 | pStat.disableFET(); 150 | pStat.setGain(lmpGain); 151 | pStat.setRLoad(0); 152 | //pStat.setRLoad(3); 153 | pStat.setExtRefSource(); 154 | pStat.setIntZ(1); 155 | pStat.setThreeLead(); 156 | pStat.setBias(0); 157 | pStat.setPosBias(); 158 | 159 | setOutputsToZero(); 160 | } 161 | 162 | 163 | void setOutputsToZero() 164 | { 165 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, 0); 166 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 167 | pStat.setBias(0); 168 | } 169 | 170 | 171 | void runSWV(uint8_t lmpGain, int16_t startV, int16_t endV, 172 | int16_t pulseAmp, int16_t stepV, double freq) 173 | { 174 | initLMP(lmpGain); 175 | stepV = abs(stepV); 176 | pulseAmp = abs(pulseAmp); 177 | freq = (uint16_t)(1000.0 / (2*freq)); 178 | 179 | 180 | if(startV < endV) runSWVForward(startV, endV, pulseAmp, stepV, freq); 181 | else runSWVBackward(startV, endV, pulseAmp, stepV, freq); 182 | 183 | //setOutputsToZero(); 184 | } 185 | 186 | 187 | void runSWVForward(int16_t startV, int16_t endV, int16_t pulseAmp, 188 | int16_t stepV, double freq) 189 | { 190 | for (int16_t j = startV; j <= endV; j += stepV) 191 | { 192 | //positive pulse 193 | biasAndSample(j + pulseAmp,freq); 194 | Serial.print(F(",")); 195 | 196 | //negative pulse 197 | biasAndSample(j - pulseAmp,freq); 198 | Serial.println(); 199 | } 200 | } 201 | 202 | 203 | 204 | void runSWVBackward(int16_t startV, int16_t endV, int16_t pulseAmp, 205 | int16_t stepV, double freq) 206 | { 207 | for (int16_t j = startV; j >= endV; j -= stepV) 208 | { 209 | //positive pulse 210 | biasAndSample(j + pulseAmp,freq); 211 | Serial.print(F(",")); 212 | 213 | //negative pulse 214 | biasAndSample(j - pulseAmp,freq); 215 | Serial.println(); 216 | } 217 | } 218 | 219 | 220 | void biasAndSample(int16_t voltage, double rate) 221 | { 222 | Serial.print(millis()); 223 | Serial.print(F(",")); 224 | Serial.print(voltage); 225 | Serial.print(F(",")); 226 | 227 | setLMPBias(voltage); 228 | setVoltage(voltage); 229 | 230 | delay(rate); 231 | sampleOutputs(); 232 | testingDiffAndINAAmps(); 233 | //delay(rate); 234 | } 235 | 236 | 237 | void sampleOutputs() 238 | { 239 | // Serial.print(analogRead(DACRead)); 240 | // Serial.print(F(",")); 241 | // Serial.print(analogRead(LMP)); 242 | // Serial.print(F(",")); 243 | 244 | // Serial.print(analogRead(C1)); 245 | // Serial.print(F(",")); 246 | // Serial.print(analogRead(C2)); 247 | 248 | 249 | // Serial.print(pStat.getVoltage(analogRead(DACRead),opVolt,adcBits),1); 250 | // Serial.print(F(",")); 251 | // Serial.print(pStat.getCurrent(analogRead(LMP),opVolt,adcBits),6); 252 | // Serial.print(F(",")); 253 | Serial.print(pStat.getCurrent(analogRead(C1),opVolt,adcBits,extGain),currentRange); 254 | Serial.print(F(",")); 255 | Serial.print(pStat.getCurrent(analogRead(C2),opVolt,adcBits,extGain),currentRange); 256 | } 257 | 258 | 259 | void testingDiffAndINAAmps() 260 | { 261 | // Serial.print(F(",")); 262 | // Serial.print(analogRead(diffAmp)); 263 | // Serial.print(F(",")); 264 | // Serial.print(analogRead(INA)); 265 | 266 | 267 | Serial.print(F(",")); 268 | Serial.print(pStat.getCurrent(analogRead(diffAmp),opVolt,adcBits,extGain),currentRange); 269 | Serial.print(F(",")); 270 | Serial.print(pStat.getCurrent(analogRead(INA),opVolt,adcBits,extGain),currentRange); 271 | } 272 | 273 | 274 | 275 | void setVoltage(int16_t voltage) 276 | { 277 | uint16_t dacVout = 1500; 278 | uint8_t bias_setting = 0; 279 | 280 | if(abs(voltage) < 15) voltage = 15*(voltage/abs(voltage)); 281 | 282 | int16_t setV = dacVout*TIA_BIAS[bias_setting]; 283 | voltage = abs(voltage); 284 | 285 | 286 | while(setV > voltage*(1+v_tolerance) || setV < voltage*(1-v_tolerance)) 287 | { 288 | if(bias_setting == 0) bias_setting = 1; 289 | 290 | dacVout = voltage/TIA_BIAS[bias_setting]; 291 | 292 | if (dacVout > opVolt) 293 | { 294 | bias_setting++; 295 | dacVout = 1500; 296 | 297 | if(bias_setting > NUM_TIA_BIAS) bias_setting = 0; 298 | } 299 | 300 | setV = dacVout*TIA_BIAS[bias_setting]; 301 | } 302 | 303 | 304 | pStat.setBias(bias_setting); 305 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, convertDACVoutToDACVal(dacVout)); 306 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 307 | 308 | 309 | if(debug) 310 | { 311 | Serial.print(bias_setting); 312 | Serial.print(F(",")); 313 | Serial.print(TIA_BIAS[bias_setting]); 314 | Serial.print(F(",")); 315 | Serial.print(dacVout); 316 | Serial.print(F(",")); 317 | //Serial.print(convertDACVoutToDACVal(dacVout)); 318 | Serial.print(convertDACVoutToDACVal(dacVout) >> 2); 319 | Serial.print(F(",")); 320 | } 321 | } 322 | 323 | 324 | //Convert the desired voltage 325 | uint16_t convertDACVoutToDACVal(uint16_t dacVout) 326 | { 327 | //return (dacVout-dacMin)*((double)dacResolution/dacSpan); 328 | return dacVout*((double)dacResolution/opVolt); 329 | } 330 | 331 | 332 | void setLMPBias(int16_t voltage) 333 | { 334 | signed char sign = (double)voltage/abs(voltage); 335 | 336 | if(sign < 0) pStat.setNegBias(); 337 | else if (sign > 0) pStat.setPosBias(); 338 | else {} //do nothing 339 | 340 | if(debug) 341 | { 342 | Serial.print(sign); 343 | Serial.print(F(",")); 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/SquareWaveVoltammetry-wAD5644R-for-FeCN/SquareWaveVoltammetry-wAD5644R-for-FeCN.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * FILENAME: SquareWaveVoltammetry-wDueDAC.ino 3 | * AUTHOR: Orlando Hoilett 4 | * VERSION: V.1.0.0 5 | * 6 | * 7 | * DESCRIPTION 8 | * 9 | * 10 | * 11 | * UPDATES 12 | * 13 | * 14 | * 15 | * DISCLAIMER 16 | * Linnes Lab code, firmware, and software is released under the 17 | * MIT License (http://opensource.org/licenses/MIT). 18 | * 19 | * The MIT License (MIT) 20 | * 21 | * Copyright (c) 2019 Linnes Lab, Purdue University 22 | * 23 | * Permission is hereby granted, free of charge, to any person 24 | * obtaining a copy of this software and associated documentation 25 | * files (the "Software"), to deal in the Software without 26 | * restriction, including without limitation the rights to use, 27 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | * copies of the Software, and to permit persons to whom the Software 29 | * is furnished to do so, subject to the following conditions: 30 | * 31 | * The above copyright notice and this permission notice shall be included 32 | * in all copies or substantial portions of the Software. 33 | * 34 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 35 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 37 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 39 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 40 | * IN THE SOFTWARE. 41 | * 42 | * 43 | */ 44 | 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include ; 51 | #include "pgmStrToRAM.h"; //SerialUSB.println(freeMemory(), DEC); // print how much RAM is available. 52 | 53 | 54 | LMP91000 pStat = LMP91000(); 55 | 56 | const int AD56X4_SS_pin = 10; 57 | 58 | const uint16_t opVolt = 3280; 59 | const uint8_t adcBits = 10; 60 | const double v_tolerance = 0.009; 61 | 62 | 63 | //const uint16_t dacMin = 0; //0V 64 | //const uint16_t dacMax = opVolt; //Vdd 65 | //const uint16_t dacSpan = opVolt; //Vdd 66 | const uint16_t dacResolution = pow(2,16)-1; //16-bit 67 | 68 | 69 | bool debug = false; 70 | 71 | 72 | //analog input pins to read voltages 73 | const uint8_t LMP = A2; 74 | const uint8_t C1 = A0; 75 | const uint8_t C2 = A1; 76 | const uint8_t DACRead = A3; 77 | const uint8_t diffAmp = A6; 78 | const uint8_t INA = A7; 79 | 80 | 81 | void setup() 82 | { 83 | Wire.begin(); 84 | Serial.begin(115200); 85 | 86 | 87 | pinMode(AD56X4_SS_pin,OUTPUT); 88 | SPI.setClockDivider(SPI_CLOCK_DIV2); 89 | SPI.begin(); 90 | 91 | AD56X4.reset(AD56X4_SS_pin,true); 92 | 93 | 94 | //enable the potentiostat 95 | delay(50); 96 | pStat.standby(); 97 | delay(50); 98 | initLMP(5); 99 | delay(2000); //warm-up time for the gas sensor 100 | 101 | } 102 | 103 | 104 | void loop() 105 | { 106 | Serial.println(F("Ready!")); 107 | 108 | //will hold the code here until a character is sent over the serial port 109 | //this ensures the experiment will only run when initiated 110 | while(!Serial.available()); 111 | Serial.read(); 112 | 113 | 114 | //prints column headings 115 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 116 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,LMP,C1,C2"); 117 | 118 | // //prints column headings 119 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 120 | // else Serial.println("Time(ms),Volt,diffAmp,INA,Time(ms),Volt,diffAmp,INA"); 121 | 122 | 123 | // //prints column headings 124 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 125 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,Time(ms),Volt,LMP,C1,C2"); 126 | 127 | 128 | //Serial.println("Time(ms),Volt,C1,C2,INA,Time(ms),Volt,C1,C2,INA"); 129 | Serial.println("Time(ms),Volt,C1,C2,Diff,INA,Time(ms),Volt,C1,C2,Diff,INA"); 130 | 131 | 132 | // //prints column headings 133 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 134 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,Diff,INA,Time(ms),Volt,LMP,C1,C2,Diff,INA"); 135 | 136 | 137 | //lmpGain, startV(mV), endV(mV), pulseAmp(mV), stepV(mV), freq(Hz) 138 | runSWV(2, 500, -500, 50, 2, 120); 139 | // Serial.println("Backward scan"); 140 | // runSWV(2, -500, 50, 50, 5, 0.8); 141 | setOutputsToZero(); 142 | } 143 | 144 | 145 | void initLMP(uint8_t lmpGain) 146 | { 147 | pStat.disableFET(); 148 | pStat.setGain(lmpGain); 149 | pStat.setRLoad(0); 150 | //pStat.setRLoad(3); 151 | pStat.setExtRefSource(); 152 | pStat.setIntZ(1); 153 | pStat.setThreeLead(); 154 | pStat.setBias(0); 155 | pStat.setPosBias(); 156 | 157 | setOutputsToZero(); 158 | } 159 | 160 | 161 | void setOutputsToZero() 162 | { 163 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, 0); 164 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 165 | pStat.setBias(0); 166 | } 167 | 168 | 169 | void runSWV(uint8_t lmpGain, int16_t startV, int16_t endV, 170 | int16_t pulseAmp, int16_t stepV, double freq) 171 | { 172 | initLMP(lmpGain); 173 | stepV = abs(stepV); 174 | pulseAmp = abs(pulseAmp); 175 | freq = (uint16_t)(1000.0 / (2*freq)); 176 | 177 | 178 | if(startV < endV) runSWVForward(startV, endV, pulseAmp, stepV, freq); 179 | else runSWVBackward(startV, endV, pulseAmp, stepV, freq); 180 | 181 | //setOutputsToZero(); 182 | } 183 | 184 | 185 | void runSWVForward(int16_t startV, int16_t endV, int16_t pulseAmp, 186 | int16_t stepV, double freq) 187 | { 188 | for (int16_t j = startV; j <= endV; j += stepV) 189 | { 190 | //positive pulse 191 | biasAndSample(j + pulseAmp,freq); 192 | Serial.print(F(",")); 193 | 194 | //negative pulse 195 | biasAndSample(j - pulseAmp,freq); 196 | Serial.println(); 197 | } 198 | } 199 | 200 | 201 | 202 | void runSWVBackward(int16_t startV, int16_t endV, int16_t pulseAmp, 203 | int16_t stepV, double freq) 204 | { 205 | for (int16_t j = startV; j >= endV; j -= stepV) 206 | { 207 | //positive pulse 208 | biasAndSample(j + pulseAmp,freq); 209 | Serial.print(F(",")); 210 | 211 | //negative pulse 212 | biasAndSample(j - pulseAmp,freq); 213 | Serial.println(); 214 | } 215 | } 216 | 217 | 218 | void biasAndSample(int16_t voltage, double rate) 219 | { 220 | Serial.print(millis()); 221 | Serial.print(F(",")); 222 | Serial.print(voltage); 223 | Serial.print(F(",")); 224 | 225 | setLMPBias(voltage); 226 | setVoltage(voltage); 227 | 228 | delay(rate); 229 | sampleOutputs(); 230 | testingDiffAndINAAmps(); 231 | //delay(rate); 232 | } 233 | 234 | 235 | void sampleOutputs() 236 | { 237 | // Serial.print(analogRead(DACRead)); 238 | // Serial.print(F(",")); 239 | // Serial.print(analogRead(LMP)); 240 | // Serial.print(F(",")); 241 | Serial.print(analogRead(C1)); 242 | Serial.print(F(",")); 243 | Serial.print(analogRead(C2)); 244 | 245 | 246 | // Serial.print(pStat.getVoltage(analogRead(DACRead),opVolt,adcBits),1); 247 | // Serial.print(F(",")); 248 | // Serial.print(pStat.getCurrent(analogRead(LMP),opVolt,adcBits),6); 249 | // Serial.print(F(",")); 250 | // Serial.print(pStat.getCurrent(analogRead(C1),opVolt,adcBits),6); 251 | // Serial.print(F(",")); 252 | // Serial.print(pStat.getCurrent(analogRead(C2),opVolt,adcBits),6); 253 | } 254 | 255 | 256 | void testingDiffAndINAAmps() 257 | { 258 | Serial.print(F(",")); 259 | Serial.print(analogRead(diffAmp)); 260 | Serial.print(F(",")); 261 | Serial.print(analogRead(INA)); 262 | 263 | 264 | // Serial.print(F(",")); 265 | // Serial.print(pStat.getCurrent(analogRead(diffAmp),opVolt,adcBits),6); 266 | // Serial.print(F(",")); 267 | // Serial.print(pStat.getCurrent(analogRead(INA),opVolt,adcBits),6); 268 | } 269 | 270 | 271 | 272 | void setVoltage(int16_t voltage) 273 | { 274 | uint16_t dacVout = 1500; 275 | uint8_t bias_setting = 0; 276 | 277 | if(abs(voltage) < 15) voltage = 15*(voltage/abs(voltage)); 278 | 279 | int16_t setV = dacVout*TIA_BIAS[bias_setting]; 280 | voltage = abs(voltage); 281 | 282 | 283 | while(setV > voltage*(1+v_tolerance) || setV < voltage*(1-v_tolerance)) 284 | { 285 | if(bias_setting == 0) bias_setting = 1; 286 | 287 | dacVout = voltage/TIA_BIAS[bias_setting]; 288 | 289 | if (dacVout > opVolt) 290 | { 291 | bias_setting++; 292 | dacVout = 1500; 293 | 294 | if(bias_setting > NUM_TIA_BIAS) bias_setting = 0; 295 | } 296 | 297 | setV = dacVout*TIA_BIAS[bias_setting]; 298 | } 299 | 300 | 301 | pStat.setBias(bias_setting); 302 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, convertDACVoutToDACVal(dacVout)); 303 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 304 | 305 | 306 | if(debug) 307 | { 308 | Serial.print(bias_setting); 309 | Serial.print(F(",")); 310 | Serial.print(TIA_BIAS[bias_setting]); 311 | Serial.print(F(",")); 312 | Serial.print(dacVout); 313 | Serial.print(F(",")); 314 | //Serial.print(convertDACVoutToDACVal(dacVout)); 315 | Serial.print(convertDACVoutToDACVal(dacVout) >> 2); 316 | Serial.print(F(",")); 317 | } 318 | } 319 | 320 | 321 | //Convert the desired voltage 322 | uint16_t convertDACVoutToDACVal(uint16_t dacVout) 323 | { 324 | //return (dacVout-dacMin)*((double)dacResolution/dacSpan); 325 | return dacVout*((double)dacResolution/opVolt); 326 | } 327 | 328 | 329 | void setLMPBias(int16_t voltage) 330 | { 331 | signed char sign = (double)voltage/abs(voltage); 332 | 333 | if(sign < 0) pStat.setNegBias(); 334 | else if (sign > 0) pStat.setPosBias(); 335 | else {} //do nothing 336 | 337 | if(debug) 338 | { 339 | Serial.print(sign); 340 | Serial.print(F(",")); 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/SquareWaveVoltammetry-wAD5644R/SquareWaveVoltammetry-wAD5644R.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * FILENAME: SquareWaveVoltammetry-wDueDAC.ino 3 | * AUTHOR: Orlando Hoilett 4 | * VERSION: V.1.0.0 5 | * 6 | * 7 | * DESCRIPTION 8 | * 9 | * 10 | * 11 | * UPDATES 12 | * 13 | * 14 | * 15 | * DISCLAIMER 16 | * Linnes Lab code, firmware, and software is released under the 17 | * MIT License (http://opensource.org/licenses/MIT). 18 | * 19 | * The MIT License (MIT) 20 | * 21 | * Copyright (c) 2019 Linnes Lab, Purdue University 22 | * 23 | * Permission is hereby granted, free of charge, to any person 24 | * obtaining a copy of this software and associated documentation 25 | * files (the "Software"), to deal in the Software without 26 | * restriction, including without limitation the rights to use, 27 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | * copies of the Software, and to permit persons to whom the Software 29 | * is furnished to do so, subject to the following conditions: 30 | * 31 | * The above copyright notice and this permission notice shall be included 32 | * in all copies or substantial portions of the Software. 33 | * 34 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 35 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 37 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 39 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 40 | * IN THE SOFTWARE. 41 | * 42 | * 43 | */ 44 | 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include ; 51 | #include "pgmStrToRAM.h"; //SerialUSB.println(freeMemory(), DEC); // print how much RAM is available. 52 | 53 | 54 | LMP91000 pStat = LMP91000(); 55 | 56 | const int AD56X4_SS_pin = 10; 57 | 58 | const uint16_t opVolt = 3280; 59 | const uint8_t adcBits = 10; 60 | const double v_tolerance = 0.009; 61 | 62 | 63 | //const uint16_t dacMin = 0; //0V 64 | //const uint16_t dacMax = opVolt; //Vdd 65 | //const uint16_t dacSpan = opVolt; //Vdd 66 | const uint16_t dacResolution = pow(2,16)-1; //16-bit 67 | 68 | 69 | bool debug = false; 70 | 71 | 72 | //analog input pins to read voltages 73 | const uint8_t LMP = A2; 74 | const uint8_t C1 = A0; 75 | const uint8_t C2 = A1; 76 | const uint8_t DACRead = A3; 77 | const uint8_t diffAmp = A6; 78 | const uint8_t INA = A7; 79 | 80 | 81 | void setup() 82 | { 83 | Wire.begin(); 84 | Serial.begin(115200); 85 | 86 | 87 | pinMode(AD56X4_SS_pin,OUTPUT); 88 | SPI.setClockDivider(SPI_CLOCK_DIV2); 89 | SPI.begin(); 90 | 91 | AD56X4.reset(AD56X4_SS_pin,true); 92 | 93 | 94 | //enable the potentiostat 95 | delay(50); 96 | pStat.standby(); 97 | delay(50); 98 | initLMP(5); 99 | delay(2000); //warm-up time for the gas sensor 100 | 101 | } 102 | 103 | 104 | void loop() 105 | { 106 | Serial.println(F("Ready!")); 107 | 108 | //will hold the code here until a character is sent over the serial port 109 | //this ensures the experiment will only run when initiated 110 | while(!Serial.available()); 111 | Serial.read(); 112 | 113 | 114 | //prints column headings 115 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 116 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,LMP,C1,C2"); 117 | 118 | // //prints column headings 119 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 120 | // else Serial.println("Time(ms),Volt,diffAmp,INA,Time(ms),Volt,diffAmp,INA"); 121 | 122 | 123 | // //prints column headings 124 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 125 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,Time(ms),Volt,LMP,C1,C2"); 126 | 127 | 128 | //Serial.println("Time(ms),Volt,C1,C2,INA,Time(ms),Volt,C1,C2,INA"); 129 | Serial.println("Time(ms),Volt,C1,C2,Diff,INA,Time(ms),Volt,C1,C2,Diff,INA"); 130 | 131 | 132 | // //prints column headings 133 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 134 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,Diff,INA,Time(ms),Volt,LMP,C1,C2,Diff,INA"); 135 | 136 | 137 | //lmpGain, startV(mV), endV(mV), pulseAmp(mV), stepV(mV), freq(Hz) 138 | runSWV(2, 50, -500, 50, 5, 0.8); 139 | Serial.println("Backward scan"); 140 | runSWV(2, -500, 50, 50, 5, 0.8); 141 | setOutputsToZero(); 142 | } 143 | 144 | 145 | void initLMP(uint8_t lmpGain) 146 | { 147 | pStat.disableFET(); 148 | pStat.setGain(lmpGain); 149 | pStat.setRLoad(0); 150 | //pStat.setRLoad(3); 151 | pStat.setExtRefSource(); 152 | pStat.setIntZ(1); 153 | pStat.setThreeLead(); 154 | pStat.setBias(0); 155 | pStat.setPosBias(); 156 | 157 | setOutputsToZero(); 158 | } 159 | 160 | 161 | void setOutputsToZero() 162 | { 163 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, 0); 164 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 165 | pStat.setBias(0); 166 | } 167 | 168 | 169 | void runSWV(uint8_t lmpGain, int16_t startV, int16_t endV, 170 | int16_t pulseAmp, int16_t stepV, double freq) 171 | { 172 | initLMP(lmpGain); 173 | stepV = abs(stepV); 174 | pulseAmp = abs(pulseAmp); 175 | freq = (uint16_t)(1000.0 / (2*freq)); 176 | 177 | 178 | if(startV < endV) runSWVForward(startV, endV, pulseAmp, stepV, freq); 179 | else runSWVBackward(startV, endV, pulseAmp, stepV, freq); 180 | 181 | //setOutputsToZero(); 182 | } 183 | 184 | 185 | void runSWVForward(int16_t startV, int16_t endV, int16_t pulseAmp, 186 | int16_t stepV, double freq) 187 | { 188 | for (int16_t j = startV; j <= endV; j += stepV) 189 | { 190 | //positive pulse 191 | biasAndSample(j + pulseAmp,freq); 192 | Serial.print(F(",")); 193 | 194 | //negative pulse 195 | biasAndSample(j - pulseAmp,freq); 196 | Serial.println(); 197 | } 198 | } 199 | 200 | 201 | 202 | void runSWVBackward(int16_t startV, int16_t endV, int16_t pulseAmp, 203 | int16_t stepV, double freq) 204 | { 205 | for (int16_t j = startV; j >= endV; j -= stepV) 206 | { 207 | //positive pulse 208 | biasAndSample(j + pulseAmp,freq); 209 | Serial.print(F(",")); 210 | 211 | //negative pulse 212 | biasAndSample(j - pulseAmp,freq); 213 | Serial.println(); 214 | } 215 | } 216 | 217 | 218 | void biasAndSample(int16_t voltage, double rate) 219 | { 220 | Serial.print(millis()); 221 | Serial.print(F(",")); 222 | Serial.print(voltage); 223 | Serial.print(F(",")); 224 | 225 | setLMPBias(voltage); 226 | setVoltage(voltage); 227 | 228 | delay(rate); 229 | sampleOutputs(); 230 | testingDiffAndINAAmps(); 231 | //delay(rate); 232 | } 233 | 234 | 235 | void sampleOutputs() 236 | { 237 | // Serial.print(analogRead(DACRead)); 238 | // Serial.print(F(",")); 239 | // Serial.print(analogRead(LMP)); 240 | // Serial.print(F(",")); 241 | Serial.print(analogRead(C1)); 242 | Serial.print(F(",")); 243 | Serial.print(analogRead(C2)); 244 | 245 | 246 | // Serial.print(pStat.getVoltage(analogRead(DACRead),opVolt,adcBits),1); 247 | // Serial.print(F(",")); 248 | // Serial.print(pStat.getCurrent(analogRead(LMP),opVolt,adcBits),6); 249 | // Serial.print(F(",")); 250 | // Serial.print(pStat.getCurrent(analogRead(C1),opVolt,adcBits),6); 251 | // Serial.print(F(",")); 252 | // Serial.print(pStat.getCurrent(analogRead(C2),opVolt,adcBits),6); 253 | } 254 | 255 | 256 | void testingDiffAndINAAmps() 257 | { 258 | Serial.print(F(",")); 259 | Serial.print(analogRead(diffAmp)); 260 | Serial.print(F(",")); 261 | Serial.print(analogRead(INA)); 262 | 263 | 264 | // Serial.print(F(",")); 265 | // Serial.print(pStat.getCurrent(analogRead(diffAmp),opVolt,adcBits),6); 266 | // Serial.print(F(",")); 267 | // Serial.print(pStat.getCurrent(analogRead(INA),opVolt,adcBits),6); 268 | } 269 | 270 | 271 | 272 | void setVoltage(int16_t voltage) 273 | { 274 | uint16_t dacVout = 1500; 275 | uint8_t bias_setting = 0; 276 | 277 | if(abs(voltage) < 15) voltage = 15*(voltage/abs(voltage)); 278 | 279 | int16_t setV = dacVout*TIA_BIAS[bias_setting]; 280 | voltage = abs(voltage); 281 | 282 | 283 | while(setV > voltage*(1+v_tolerance) || setV < voltage*(1-v_tolerance)) 284 | { 285 | if(bias_setting == 0) bias_setting = 1; 286 | 287 | dacVout = voltage/TIA_BIAS[bias_setting]; 288 | 289 | if (dacVout > opVolt) 290 | { 291 | bias_setting++; 292 | dacVout = 1500; 293 | 294 | if(bias_setting > NUM_TIA_BIAS) bias_setting = 0; 295 | } 296 | 297 | setV = dacVout*TIA_BIAS[bias_setting]; 298 | } 299 | 300 | 301 | pStat.setBias(bias_setting); 302 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, convertDACVoutToDACVal(dacVout)); 303 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 304 | 305 | 306 | if(debug) 307 | { 308 | Serial.print(bias_setting); 309 | Serial.print(F(",")); 310 | Serial.print(TIA_BIAS[bias_setting]); 311 | Serial.print(F(",")); 312 | Serial.print(dacVout); 313 | Serial.print(F(",")); 314 | //Serial.print(convertDACVoutToDACVal(dacVout)); 315 | Serial.print(convertDACVoutToDACVal(dacVout) >> 2); 316 | Serial.print(F(",")); 317 | } 318 | } 319 | 320 | 321 | //Convert the desired voltage 322 | uint16_t convertDACVoutToDACVal(uint16_t dacVout) 323 | { 324 | //return (dacVout-dacMin)*((double)dacResolution/dacSpan); 325 | return dacVout*((double)dacResolution/opVolt); 326 | } 327 | 328 | 329 | void setLMPBias(int16_t voltage) 330 | { 331 | signed char sign = (double)voltage/abs(voltage); 332 | 333 | if(sign < 0) pStat.setNegBias(); 334 | else if (sign > 0) pStat.setPosBias(); 335 | else {} //do nothing 336 | 337 | if(debug) 338 | { 339 | Serial.print(sign); 340 | Serial.print(F(",")); 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/examples/from-au9-swv-pbs-2019-02-05-0034/from-au9-swv-pbs-2019-02-05-0034.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * FILENAME: SquareWaveVoltammetry-wDueDAC.ino 3 | * AUTHOR: Orlando Hoilett 4 | * VERSION: V.1.0.0 5 | * 6 | * 7 | * DESCRIPTION 8 | * 9 | * 10 | * 11 | * UPDATES 12 | * 13 | * 14 | * 15 | * DISCLAIMER 16 | * Linnes Lab code, firmware, and software is released under the 17 | * MIT License (http://opensource.org/licenses/MIT). 18 | * 19 | * The MIT License (MIT) 20 | * 21 | * Copyright (c) 2019 Linnes Lab, Purdue University 22 | * 23 | * Permission is hereby granted, free of charge, to any person 24 | * obtaining a copy of this software and associated documentation 25 | * files (the "Software"), to deal in the Software without 26 | * restriction, including without limitation the rights to use, 27 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | * copies of the Software, and to permit persons to whom the Software 29 | * is furnished to do so, subject to the following conditions: 30 | * 31 | * The above copyright notice and this permission notice shall be included 32 | * in all copies or substantial portions of the Software. 33 | * 34 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 35 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 37 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 39 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 40 | * IN THE SOFTWARE. 41 | * 42 | * 43 | */ 44 | 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include ; 51 | #include "pgmStrToRAM.h"; //SerialUSB.println(freeMemory(), DEC); // print how much RAM is available. 52 | 53 | 54 | LMP91000 pStat = LMP91000(); 55 | 56 | const int AD56X4_SS_pin = 10; 57 | 58 | const uint16_t opVolt = 3280; 59 | const uint8_t adcBits = 10; 60 | const double v_tolerance = 0.009; 61 | const uint8_t currentRange = 9; 62 | const double extGain = 180000; 63 | 64 | 65 | //const uint16_t dacMin = 0; //0V 66 | //const uint16_t dacMax = opVolt; //Vdd 67 | //const uint16_t dacSpan = opVolt; //Vdd 68 | const uint16_t dacResolution = pow(2,16)-1; //16-bit 69 | 70 | 71 | bool debug = false; 72 | 73 | 74 | //analog input pins to read voltages 75 | const uint8_t LMP = A2; 76 | const uint8_t C1 = A0; 77 | const uint8_t C2 = A1; 78 | const uint8_t DACRead = A3; 79 | const uint8_t diffAmp = A6; 80 | const uint8_t INA = A7; 81 | 82 | 83 | void setup() 84 | { 85 | Wire.begin(); 86 | Serial.begin(115200); 87 | 88 | 89 | pinMode(AD56X4_SS_pin,OUTPUT); 90 | SPI.setClockDivider(SPI_CLOCK_DIV2); 91 | SPI.begin(); 92 | 93 | AD56X4.reset(AD56X4_SS_pin,true); 94 | 95 | 96 | //enable the potentiostat 97 | delay(50); 98 | pStat.standby(); 99 | delay(50); 100 | initLMP(5); 101 | delay(2000); //warm-up time for the gas sensor 102 | 103 | } 104 | 105 | 106 | void loop() 107 | { 108 | Serial.println(F("Ready!")); 109 | 110 | //will hold the code here until a character is sent over the serial port 111 | //this ensures the experiment will only run when initiated 112 | while(!Serial.available()); 113 | Serial.read(); 114 | 115 | 116 | //prints column headings 117 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 118 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,LMP,C1,C2"); 119 | 120 | // //prints column headings 121 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 122 | // else Serial.println("Time(ms),Volt,diffAmp,INA,Time(ms),Volt,diffAmp,INA"); 123 | 124 | 125 | // //prints column headings 126 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 127 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,Time(ms),Volt,LMP,C1,C2"); 128 | 129 | 130 | //Serial.println("Time(ms),Volt,C1,C2,INA,Time(ms),Volt,C1,C2,INA"); 131 | Serial.println("Time(ms),Volt,C1,C2,Diff,INA,Time(ms),Volt,C1,C2,Diff,INA"); 132 | 133 | 134 | // //prints column headings 135 | // if(debug) Serial.println("Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA,Time(ms),VF,Sign,Bias Index,Bias,dacVOut,DACVal,DAC Read,LMP,C1,C2,DiffAmp,INA"); 136 | // else Serial.println("Time(ms),Volt,LMP,C1,C2,Diff,INA,Time(ms),Volt,LMP,C1,C2,Diff,INA"); 137 | 138 | 139 | //lmpGain, startV(mV), endV(mV), pulseAmp(mV), stepV(mV), freq(Hz) 140 | runSWV(0, 50, -500, 50, 5, 120); 141 | Serial.println("Backward scan"); 142 | runSWV(0, -500, 50, 50, 5, 120); 143 | setOutputsToZero(); 144 | } 145 | 146 | 147 | void initLMP(uint8_t lmpGain) 148 | { 149 | pStat.disableFET(); 150 | pStat.setGain(lmpGain); 151 | pStat.setRLoad(0); 152 | //pStat.setRLoad(3); 153 | pStat.setExtRefSource(); 154 | pStat.setIntZ(1); 155 | pStat.setThreeLead(); 156 | pStat.setBias(0); 157 | pStat.setPosBias(); 158 | 159 | setOutputsToZero(); 160 | } 161 | 162 | 163 | void setOutputsToZero() 164 | { 165 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, 0); 166 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 167 | pStat.setBias(0); 168 | } 169 | 170 | 171 | void runSWV(uint8_t lmpGain, int16_t startV, int16_t endV, 172 | int16_t pulseAmp, int16_t stepV, double freq) 173 | { 174 | initLMP(lmpGain); 175 | stepV = abs(stepV); 176 | pulseAmp = abs(pulseAmp); 177 | freq = (uint16_t)(1000.0 / (2*freq)); 178 | 179 | 180 | if(startV < endV) runSWVForward(startV, endV, pulseAmp, stepV, freq); 181 | else runSWVBackward(startV, endV, pulseAmp, stepV, freq); 182 | 183 | //setOutputsToZero(); 184 | } 185 | 186 | 187 | void runSWVForward(int16_t startV, int16_t endV, int16_t pulseAmp, 188 | int16_t stepV, double freq) 189 | { 190 | for (int16_t j = startV; j <= endV; j += stepV) 191 | { 192 | //positive pulse 193 | biasAndSample(j + pulseAmp,freq); 194 | Serial.print(F(",")); 195 | 196 | //negative pulse 197 | biasAndSample(j - pulseAmp,freq); 198 | Serial.println(); 199 | } 200 | } 201 | 202 | 203 | 204 | void runSWVBackward(int16_t startV, int16_t endV, int16_t pulseAmp, 205 | int16_t stepV, double freq) 206 | { 207 | for (int16_t j = startV; j >= endV; j -= stepV) 208 | { 209 | //positive pulse 210 | biasAndSample(j + pulseAmp,freq); 211 | Serial.print(F(",")); 212 | 213 | //negative pulse 214 | biasAndSample(j - pulseAmp,freq); 215 | Serial.println(); 216 | } 217 | } 218 | 219 | 220 | void biasAndSample(int16_t voltage, double rate) 221 | { 222 | Serial.print(millis()); 223 | Serial.print(F(",")); 224 | Serial.print(voltage); 225 | Serial.print(F(",")); 226 | 227 | setLMPBias(voltage); 228 | setVoltage(voltage); 229 | 230 | delay(rate); 231 | sampleOutputs(); 232 | testingDiffAndINAAmps(); 233 | //delay(rate); 234 | } 235 | 236 | 237 | void sampleOutputs() 238 | { 239 | // Serial.print(analogRead(DACRead)); 240 | // Serial.print(F(",")); 241 | // Serial.print(analogRead(LMP)); 242 | // Serial.print(F(",")); 243 | 244 | // Serial.print(analogRead(C1)); 245 | // Serial.print(F(",")); 246 | // Serial.print(analogRead(C2)); 247 | 248 | 249 | // Serial.print(pStat.getVoltage(analogRead(DACRead),opVolt,adcBits),1); 250 | // Serial.print(F(",")); 251 | // Serial.print(pStat.getCurrent(analogRead(LMP),opVolt,adcBits),6); 252 | // Serial.print(F(",")); 253 | Serial.print(pStat.getCurrent(analogRead(C1),opVolt,adcBits,extGain),currentRange); 254 | Serial.print(F(",")); 255 | Serial.print(pStat.getCurrent(analogRead(C2),opVolt,adcBits,extGain),currentRange); 256 | } 257 | 258 | 259 | void testingDiffAndINAAmps() 260 | { 261 | // Serial.print(F(",")); 262 | // Serial.print(analogRead(diffAmp)); 263 | // Serial.print(F(",")); 264 | // Serial.print(analogRead(INA)); 265 | 266 | 267 | Serial.print(F(",")); 268 | Serial.print(pStat.getCurrent(analogRead(diffAmp),opVolt,adcBits,extGain),currentRange); 269 | Serial.print(F(",")); 270 | Serial.print(pStat.getCurrent(analogRead(INA),opVolt,adcBits,extGain),currentRange); 271 | } 272 | 273 | 274 | 275 | void setVoltage(int16_t voltage) 276 | { 277 | uint16_t dacVout = 1500; 278 | uint8_t bias_setting = 0; 279 | 280 | if(abs(voltage) < 15) voltage = 15*(voltage/abs(voltage)); 281 | 282 | int16_t setV = dacVout*TIA_BIAS[bias_setting]; 283 | voltage = abs(voltage); 284 | 285 | 286 | while(setV > voltage*(1+v_tolerance) || setV < voltage*(1-v_tolerance)) 287 | { 288 | if(bias_setting == 0) bias_setting = 1; 289 | 290 | dacVout = voltage/TIA_BIAS[bias_setting]; 291 | 292 | if (dacVout > opVolt) 293 | { 294 | bias_setting++; 295 | dacVout = 1500; 296 | 297 | if(bias_setting > NUM_TIA_BIAS) bias_setting = 0; 298 | } 299 | 300 | setV = dacVout*TIA_BIAS[bias_setting]; 301 | } 302 | 303 | 304 | pStat.setBias(bias_setting); 305 | AD56X4.setChannel(AD56X4_SS_pin, AD56X4_SETMODE_INPUT, AD56X4_CHANNEL_B, convertDACVoutToDACVal(dacVout)); 306 | AD56X4.updateChannel(AD56X4_SS_pin, AD56X4_CHANNEL_B); 307 | 308 | 309 | if(debug) 310 | { 311 | Serial.print(bias_setting); 312 | Serial.print(F(",")); 313 | Serial.print(TIA_BIAS[bias_setting]); 314 | Serial.print(F(",")); 315 | Serial.print(dacVout); 316 | Serial.print(F(",")); 317 | //Serial.print(convertDACVoutToDACVal(dacVout)); 318 | Serial.print(convertDACVoutToDACVal(dacVout) >> 2); 319 | Serial.print(F(",")); 320 | } 321 | } 322 | 323 | 324 | //Convert the desired voltage 325 | uint16_t convertDACVoutToDACVal(uint16_t dacVout) 326 | { 327 | //return (dacVout-dacMin)*((double)dacResolution/dacSpan); 328 | return dacVout*((double)dacResolution/opVolt); 329 | } 330 | 331 | 332 | void setLMPBias(int16_t voltage) 333 | { 334 | signed char sign = (double)voltage/abs(voltage); 335 | 336 | if(sign < 0) pStat.setNegBias(); 337 | else if (sign > 0) pStat.setPosBias(); 338 | else {} //do nothing 339 | 340 | if(debug) 341 | { 342 | Serial.print(sign); 343 | Serial.print(F(",")); 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for LMP91000 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | LMP91000 KEYWORD1 10 | 11 | 12 | 13 | ####################################### 14 | # Methods and Functions (KEYWORD2) 15 | ####################################### 16 | 17 | setMENB KEYWORD2 18 | getMENB KEYWORD2 19 | setTempSensor KEYWORD2 20 | getTempSensor KEYWORD2 21 | write KEYWORD2 22 | read KEYWORD2 23 | enable KEYWORD2 24 | disable KEYWORD2 25 | isReady KEYWORD2 26 | lock KEYWORD2 27 | unlock KEYWORD2 28 | isLocked KEYWORD2 29 | setGain KEYWORD2 30 | getGain KEYWORD2 31 | setRLoad KEYWORD2 32 | setRefSource KEYWORD2 33 | setIntRefSource KEYWORD2 34 | setExtRefSource KEYWORD2 35 | setIntZ KEYWORD2 36 | getIntZ KEYWORD2 37 | setBiasSign KEYWORD2 38 | setNegBias KEYWORD2 39 | setPosBias KEYWORD2 40 | setBias KEYWORD2 41 | setFET KEYWORD2 42 | disableFET KEYWORD2 43 | enableFET KEYWORD2 44 | setMode KEYWORD2 45 | sleep KEYWORD2 46 | setTwoLead KEYWORD2 47 | standby KEYWORD2 48 | setThreeLead KEYWORD2 49 | measureCell KEYWORD2 50 | getTemp KEYWORD2 51 | getOutput KEYWORD2 52 | getVoltage KEYWORD2 53 | getCurrent KEYWORD2 54 | 55 | 56 | 57 | ####################################### 58 | # Constants (LITERAL1) 59 | ####################################### 60 | 61 | TEMP_INTERCEPT LITERAL1 62 | TEMPSLOPE LITERAL1 63 | LMP91000_I2C_ADDRESS LITERAL1 64 | 65 | LMP91000_STATUS_REG LITERAL1 66 | LMP91000_LOCK_REG=0x01 LITERAL1 67 | LMP91000_TIACN_REG=0x10 LITERAL1 68 | LMP91000_REFCN_REG=0x11 LITERAL1 69 | LMP91000_MODECN_REG=0x12 LITERAL1 70 | 71 | LMP91000_READY LITERAL1 72 | LMP91000_NOT_READY LITERAL1 73 | 74 | LMP91000_TIA_GAIN_EXT LITERAL1 75 | LMP91000_TIA_GAIN_2P75K LITERAL1 76 | LMP91000_TIA_GAIN_3P5K LITERAL1 77 | LMP91000_TIA_GAIN_7K LITERAL1 78 | LMP91000_TIA_GAIN_14K LITERAL1 79 | LMP91000_TIA_GAIN_35K LITERAL1 80 | LMP91000_TIA_GAIN_120K LITERAL1 81 | LMP91000_TIA_GAIN_350K LITERAL1 82 | 83 | LMP91000_RLOAD_10OHM LITERAL1 84 | LMP91000_RLOAD_33OHM LITERAL1 85 | LMP91000_RLOAD_50OHM LITERAL1 86 | LMP91000_RLOAD_100OHM LITERAL1 87 | 88 | LMP91000_REF_SOURCE_INT LITERAL1 89 | LMP91000_REF_SOURCE_EXT LITERAL1 90 | 91 | LMP91000_INT_Z_20PCT LITERAL1 92 | LMP91000_INT_Z_50PCT LITERAL1 93 | LMP91000_INT_Z_67PCT LITERAL1 94 | LMP91000_INT_Z_BYPASS LITERAL1 95 | 96 | LMP91000_BIAS_SIGN_NEG LITERAL1 97 | LMP91000_BIAS_SIGN_POS LITERAL1 98 | 99 | LMP91000_BIAS_0PCT LITERAL1 100 | LMP91000_BIAS_1PCT LITERAL1 101 | LMP91000_BIAS_2PCT LITERAL1 102 | LMP91000_BIAS_4PCT LITERAL1 103 | LMP91000_BIAS_6PCT LITERAL1 104 | LMP91000_BIAS_8PCT LITERAL1 105 | LMP91000_BIAS_10PCT LITERAL1 106 | LMP91000_BIAS_12PCT LITERAL1 107 | LMP91000_BIAS_14PCT LITERAL1 108 | LMP91000_BIAS_16PCT LITERAL1 109 | LMP91000_BIAS_18PCT LITERAL1 110 | LMP91000_BIAS_20PCT LITERAL1 111 | LMP91000_BIAS_22PCT LITERAL1 112 | LMP91000_BIAS_24PCT LITERAL1 113 | 114 | LMP91000_FET_SHORT_DISABLED LITERAL1 115 | LMP91000_FET_SHORT_ENABLED LITERAL1 116 | LMP91000_OP_MODE_DEEP_SLEEP LITERAL1 117 | LMP91000_OP_MODE_GALVANIC LITERAL1 118 | LMP91000_OP_MODE_STANDBY LITERAL1 119 | LMP91000_OP_MODE_AMPEROMETRIC LITERAL1 120 | LMP91000_OP_MODE_TIA_OFF LITERAL1 121 | LMP91000_OP_MODE_TIA_ON LITERAL1 122 | 123 | LMP91000_WRITE_LOCK LITERAL1 124 | LMP91000_WRITE_UNLOCK LITERAL1 125 | 126 | LMP91000_NOT_PRESENT LITERAL1 127 | 128 | TIA_GAIN LITERAL1 129 | TIA_BIAS LITERAL1 130 | NUM_TIA_BIAS LITERAL1 131 | TIA_ZERO LITERAL1 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/LMP91000/lmp91000.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Firmware/libraries/LMP91000/lmp91000.pdf -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/MiniStatAnalyst/MiniStatAnalyst.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | FILENAME: MiniStatAnalyst.cpp 3 | AUTHOR: Orlando S. Hoilett 4 | CONTACT: ohoilett@purdue.edu 5 | VERSION: 1.0.1 6 | 7 | 8 | AFFILIATIONS 9 | Linnes Lab, Weldon School of Biomedical Engineering, 10 | Purdue University, West Lafayette, IN 47907 11 | 12 | 13 | DESCRIPTION 14 | 15 | 16 | 17 | UPDATES 18 | Version 1.0.1 19 | 2020/03/12:1748> 20 | Updated descriptions and comments. Also reorganized folder structures 21 | to comply wit Arduino library guidelines by using "extras" folder 22 | to store supplementary info for the class. 23 | 24 | 25 | 26 | DISCLAIMER 27 | Linnes Lab code, firmware, and software is released under the 28 | MIT License (http://opensource.org/licenses/MIT). 29 | 30 | The MIT License (MIT) 31 | 32 | Copyright (c) 2019 Linnes Lab, Purdue University 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy of 35 | this software and associated documentation files (the "Software"), to deal in 36 | the Software without restriction, including without limitation the rights to 37 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 38 | of the Software, and to permit persons to whom the Software is furnished to do 39 | so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all 42 | copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 50 | SOFTWARE. 51 | 52 | */ 53 | 54 | 55 | 56 | #include "MiniStatAnalyst.h" 57 | 58 | 59 | //DEFAULT CONSTRUCTOR 60 | //MiniStatAnalyst::MiniStatAnalyst() 61 | //Initializes class object 62 | // 63 | MiniStatAnalyst::MiniStatAnalyst() 64 | { 65 | } 66 | 67 | 68 | //num1 and num2 two points 69 | //dt difference in time between the two points of interest 70 | //float intentionally using float since it uses less space than double 71 | // 72 | //Calculates the derviate between two points 73 | float MiniStatAnalyst::calcDerivative(int16_t num1, int16_t num2, int16_t dt) 74 | { 75 | return (num2-num1)/(float)dt; 76 | } 77 | 78 | 79 | //uint32_t MiniStatAnalyst::calcResiduals(uint16_t samples) 80 | //{ 81 | // uint32_t sum = 0; 82 | // for (uint16_t i = 0; i < samples; i++) 83 | // { 84 | // sum += pow(num2-num1,2); 85 | // } 86 | // 87 | // return sum; 88 | //} 89 | 90 | 91 | 92 | // 93 | //Private helper method (should not be used outside of the class) that rearranges 94 | //the bounds 95 | void MiniStatAnalyst::checkVBounds(int16_t &v1, int16_t &v2, const int8_t redox) 96 | { 97 | //for the reduction scan, v1 must be larger than v2 since reduction goes 98 | //from more positive number to more negative number 99 | if(redox == REDUCTION) 100 | { 101 | if(v2 > v1) 102 | { 103 | int16_t temp = v1; //create temp variable to facilitate swapping 104 | v1 = v2; 105 | v2 = temp; 106 | } 107 | } 108 | //for the oxidation scan, v1 must be smaller than v2 since oxiation goes 109 | //from more negative number to more positive number 110 | else if(redox == OXIDATION) 111 | { 112 | if(v1 > v2) 113 | { 114 | int16_t temp = v1; //create temp variable to facilitate swapping 115 | v1 = v2; 116 | v2 = temp; 117 | } 118 | } 119 | } 120 | 121 | 122 | void MiniStatAnalyst::setBounds(const uint8_t &exp, int16_t &v1, int16_t &v2, int8_t &redox) 123 | { 124 | 125 | } 126 | 127 | 128 | //This seems useful to know https://en.wikipedia.org/wiki/Linear_least_squares#Example, 129 | //although this is really the guide this method is based on https://youtu.be/1C3olrs1CUw 130 | //void MiniStatAnalyst::calcTangentLine(Queue current_copy, 131 | // Queue voltage_copy, 132 | // float &slope, float &intercept) 133 | uint16_t MiniStatAnalyst::calcBaseline(int16_t v1, int16_t v2, int8_t redox, 134 | const float current[], const int16_t voltage[], 135 | float &slope, float &intercept, uint16_t samples) 136 | { 137 | if(redox != REDUCTION || redox != OXIDATION) return NO_REDOX_TYPE; 138 | 139 | if(v2 == v1) return NO_BASELINE_SELECTED; 140 | else checkVBounds(v1, v2, redox); 141 | 142 | //Find the voltage increments for the voltammogram. This is used later to 143 | //find when the user-selected baseline region, v1 and v2, are found in the 144 | //data stored in the voltage array 145 | int16_t v_incr = abs(voltage[0]-voltage[1]); 146 | 147 | 148 | //initializes working variables 149 | uint16_t i = 0; 150 | float xy_sum = 0; 151 | float x_sum = 0; 152 | float y_sum = 0; 153 | float x_squared_sum = 0; 154 | 155 | 156 | //Searches for the index holding the start bounds of the baseline. 157 | //Two things must be true: 158 | //1. The current voltage in the array must be within one index of the start 159 | // specified start-bound 160 | //2. The section of the array being scanned must be the correct redox type. 161 | // For reduction, the voltages should be decreasing. For oxidation, the 162 | // voltages should be increasing. 163 | while(!(v1 >= voltage[i] - v_incr && v1 <= voltage[i] + v_incr 164 | && (voltage[i+1]-voltage[i])/abs(voltage[i+1]-voltage[i]) == redox)) 165 | { 166 | i++; 167 | if(i > samples) return NO_BASELINE_FOUND; 168 | } 169 | 170 | 171 | //count variable to keep track of how many values are involved in the 172 | //subsequent calculations. This is not the same as the array index as the 173 | //array will hold values other values other than the baseline 174 | uint16_t count = 0; 175 | while(!(v2 >= voltage[i] - v_incr && v2 <= voltage[i] + v_incr) && (i+1) < samples) 176 | { 177 | xy_sum += voltage[i]*current[i]; 178 | x_sum += voltage[i]; 179 | y_sum += current[i]; 180 | x_squared_sum += voltage[i]*voltage[i]; 181 | 182 | i++; 183 | count++; 184 | if(i > samples) return NO_BASELINE_FOUND; 185 | } 186 | 187 | 188 | //Updates variables. Since slope and intercept were passed by reference, 189 | //there is no need to return them using the "return" statement. The return 190 | //statement in this function instead returns error/success codes. 191 | slope = (xy_sum - (x_sum*y_sum/(float)count))/(x_squared_sum - (x_sum*x_sum/(float)count)); 192 | intercept = y_sum/(float)count - slope*x_sum/(float)count; 193 | 194 | //if you get this far, then everything worked correctly 195 | return NO_ERROR; 196 | } 197 | 198 | 199 | 200 | // 201 | // 202 | //exp stored baseline regions for voltammograms of common redox moieties 203 | //current array storing the currents from the input voltammogram 204 | //voltage array storing the voltages from the input voltammogram 205 | //slope variable for storing the value of the baseline's slope from 206 | // the equation y = mx + b where m is the slope 207 | //intercept variable for storing the value of the baseline's intercept from 208 | // the equation y = mx + b where b is the intercept 209 | //samples number of possible values to analyze before returning the 210 | // baseline information or returning an error if no baseline is found 211 | // 212 | //Calculates the tangent to the baseline preceding the redox peak. The baseline 213 | //is calculated by using the least squared method. To calculate the baseline, 214 | //this method determines the slope and the intercept of the line and updates 215 | //the respective variables that were passed by reference. 216 | // 217 | uint16_t MiniStatAnalyst::calcBaseline(uint8_t exp, const float current[], 218 | const int16_t voltage[], float &slope, 219 | float &intercept, uint16_t samples) 220 | { 221 | //Initializes working variables 222 | int8_t redox = 0; 223 | int16_t v1 = 0; 224 | int16_t v2 = 0; 225 | int16_t v_incr = abs(voltage[0]-voltage[1]); 226 | 227 | //uses the experiment keywords to automatically set the range for 228 | //the baseline based on previous knowledge of the resulting voltammograms 229 | // 230 | //MB Aptamer is a DNA Aptamer that uses methylene blue as its redox moiety. 231 | //FeCN is potassium ferricyanide. 232 | //OX is oxidation and RED is reduction. 233 | if(exp == MB_APTAMER_BASE) 234 | { 235 | redox = REDUCTION; 236 | v1 = -50; 237 | v2 = -200; 238 | } 239 | else if (exp == FeCN_OX_BASE) 240 | { 241 | redox = OXIDATION; 242 | v1 = -185; 243 | v2 = -30; 244 | } 245 | else if (exp == FeCN_RED_BASE) 246 | { 247 | redox = REDUCTION; 248 | v1 = 400; 249 | v2 = 300; 250 | } 251 | else return NO_BASELINE_SELECTED; 252 | 253 | 254 | //initializes working variables 255 | uint16_t i = 0; 256 | float xy_sum = 0; 257 | float x_sum = 0; 258 | float y_sum = 0; 259 | float x_squared_sum = 0; 260 | 261 | 262 | //Searches for the index holding the start bounds of the baseline. 263 | //Two things must be true: 264 | //1. The current voltage in the array must be within one index of the start 265 | // specified start-bound 266 | //2. The section of the array being scanned must be the correct redox type. 267 | // For reduction, the voltages should be decreasing. For oxidation, the 268 | // voltages should be increasing. 269 | while(!(v1 >= voltage[i] - v_incr && v1 <= voltage[i] + v_incr 270 | && (voltage[i+1]-voltage[i])/abs(voltage[i+1]-voltage[i]) == redox)) 271 | { 272 | i++; 273 | if(i > samples) return NO_BASELINE_FOUND; 274 | } 275 | 276 | 277 | //count variable to keep track of how many values are involved in the 278 | //subsequent calculations. This is not the same as the array index as the 279 | //array will hold values other values other than the baseline 280 | uint16_t count = 0; 281 | while(!(v2 >= voltage[i] - v_incr && v2 <= voltage[i] + v_incr) && (i+1) < samples) 282 | { 283 | xy_sum += voltage[i]*current[i]; 284 | x_sum += voltage[i]; 285 | y_sum += current[i]; 286 | x_squared_sum += voltage[i]*voltage[i]; 287 | 288 | i++; 289 | count++; 290 | if(i > samples) return NO_BASELINE_FOUND; 291 | } 292 | 293 | 294 | //Updates variables. Since slope and intercept were passed by reference, 295 | //there is no need to return them using the "return" statement. The return 296 | //statement in this function instead returns error/success codes. 297 | slope = (xy_sum - (x_sum*y_sum/(float)count))/(x_squared_sum - (x_sum*x_sum/(float)count)); 298 | intercept = y_sum/(float)count - slope*x_sum/(float)count; 299 | 300 | //if you get this far, then everything worked correctly 301 | return NO_ERROR; 302 | } 303 | 304 | 305 | 306 | //float MiniStatAnalyst::getPeakCurrent(Queue current_copy, 307 | // Queue voltage_copy, 308 | // float slope, float intercept) 309 | uint16_t MiniStatAnalyst::getPeakCurrent(int16_t v1, int16_t v2, int8_t redox, 310 | const float current[], const int16_t voltage[], 311 | const float slope, const float intercept, 312 | float &peak, int16_t &v_peak, uint16_t samples) 313 | { 314 | if(redox != REDUCTION || redox != OXIDATION) return NO_REDOX_TYPE; 315 | 316 | if(v2 == v1) return NO_PEAK_REGION_SELECTED; 317 | checkVBounds(v1, v2, redox); 318 | 319 | int16_t v_incr = abs(voltage[0]-voltage[1]); 320 | 321 | 322 | //Searches for the index holding the start bounds of the peak region. 323 | //Two things must be true: 324 | //1. The current voltage in the array must be within one index of the start 325 | // specified start-bound 326 | //2. The section of the array being scanned must be the correct redox type. 327 | // For reduction, the voltages should be decreasing. For oxidation, the 328 | // voltages should be increasing. 329 | uint16_t i = 0; 330 | while(!(v1 >= voltage[i] - v_incr && v1 <= voltage[i] + v_incr 331 | && (voltage[i+1]-voltage[i])/abs(voltage[i+1]-voltage[i]) == redox)) 332 | { 333 | i++; 334 | if(i > samples) return NO_BASELINE_FOUND; 335 | } 336 | 337 | 338 | //Set the current index as the peak, then increment the index. 339 | peak = current[i] - (slope*voltage[i]+intercept); 340 | v_peak = voltage[i]; 341 | i++; 342 | 343 | 344 | //Runs until we reach the final bound or the index is incremented greater 345 | //than the number of samples in the range of the peak region. 346 | while(!(v2 >= voltage[i] - v_incr && v2 <= voltage[i] + v_incr) && (i+1) < samples) 347 | { 348 | //If the current index is contains a value greater than the absolute 349 | //value of the peak, then update the value of the peak. 350 | if(abs(peak) < abs(current[i] - (slope*voltage[i]+intercept))) 351 | { 352 | peak = current[i] - (slope*voltage[i]+intercept); 353 | v_peak = voltage[i]; 354 | } 355 | 356 | i++; 357 | if(i > samples) return NO_BASELINE_FOUND; 358 | } 359 | 360 | 361 | return NO_ERROR; 362 | } 363 | 364 | 365 | 366 | //exp stored peak regions for voltammograms of common redox moieties 367 | //current array storing the currents from the input voltammogram 368 | //voltage array storing the voltages from the input voltammogram 369 | //slope slope of the tangent to the baseline from the equation 370 | // y = mx + b where m is the slope 371 | //intercept y-intercept of the tangent to the baseline from the equation 372 | // y = mx + b where b is the y-intercept 373 | //samples number of possible values to analyze before returning the peak 374 | // or returning an error if no peak is found 375 | // 376 | //Obtains the peak current of the input voltammogram with respect to the tangent 377 | //to the baseline. 378 | // 379 | uint16_t MiniStatAnalyst::getPeakCurrent(uint8_t exp, const float current[], 380 | const int16_t voltage[], const float slope, 381 | const float intercept, float &peak, 382 | int16_t &v_peak, uint16_t samples) 383 | { 384 | //initializes working variables 385 | int8_t redox = 0; 386 | int16_t v1 = 0; 387 | int16_t v2 = 0; 388 | int16_t v_incr = abs(voltage[0]-voltage[1]); 389 | 390 | 391 | //uses the experiment keywords to automatically set the range for 392 | //the redox peak based on previous knowledge of the resulting voltammograms 393 | //voltages are in milliVolts 394 | // 395 | //MB Aptamer is a DNA Aptamer that uses methylene blue as its redox moiety. 396 | //FeCN is potassium ferricyanide. 397 | //OX is oxidation and RED is reduction. 398 | if(exp == MB_APTAMER_PEAK) 399 | { 400 | redox = REDUCTION; 401 | v1 = -315; 402 | v2 = -375; 403 | } 404 | else if (exp == FeCN_OX_PEAK) 405 | { 406 | redox = OXIDATION; 407 | v1 = 140; 408 | v2 = 300; 409 | } 410 | else if (exp == FeCN_RED_PEAK) 411 | { 412 | redox = REDUCTION; 413 | v1 = 170; 414 | v2 = 30; 415 | } 416 | else return NO_BASELINE_SELECTED; 417 | 418 | 419 | //Searches for the index holding the start bounds of the peak region. 420 | //Two things must be true: 421 | //1. The current voltage in the array must be within one index of the start 422 | // specified start-bound 423 | //2. The section of the array being scanned must be the correct redox type. 424 | // For reduction, the voltages should be decreasing. For oxidation, the 425 | // voltages should be increasing. 426 | uint16_t i = 0; 427 | while(!(v1 >= voltage[i] - v_incr && v1 <= voltage[i] + v_incr 428 | && (voltage[i+1]-voltage[i])/abs(voltage[i+1]-voltage[i]) == redox)) 429 | { 430 | i++; 431 | if(i > samples) return NO_BASELINE_FOUND; 432 | } 433 | 434 | 435 | //Set the current index as the peak, then increment the index. 436 | peak = current[i] - (slope*voltage[i]+intercept); 437 | v_peak = voltage[i]; 438 | i++; 439 | 440 | 441 | //Runs until we reach the final bound or the index is incremented greater 442 | //than the number of samples in the range of the peak region. 443 | while(!(v2 >= voltage[i] - v_incr && v2 <= voltage[i] + v_incr) && (i+1) < samples) 444 | { 445 | //If the current index is contains a value greater than the absolute 446 | //value of the peak, then update the value of the peak. 447 | if(abs(peak) < abs(current[i] - (slope*voltage[i]+intercept))) 448 | { 449 | peak = current[i] - (slope*voltage[i]+intercept); 450 | v_peak = voltage[i]; 451 | } 452 | 453 | i++; 454 | if(i > samples) return NO_BASELINE_FOUND; 455 | } 456 | 457 | 458 | return NO_ERROR; 459 | } 460 | 461 | 462 | //Γ=(N_A Q)/nFA 463 | float MiniStatAnalyst::calcProbeDensity(float charge, uint8_t num_electrons, float area) 464 | { 465 | return (AVOGADRO*charge)/(num_electrons*FARADAY*area); 466 | } 467 | 468 | 469 | //Q = i*V/v 470 | float MiniStatAnalyst::calcCharge(float current, int16_t voltage, uint16_t scan_rate) 471 | { 472 | return current*voltage/scan_rate; 473 | } 474 | 475 | 476 | //float MiniStatAnalyst::calcArea(float charge) 477 | //@param charge: 478 | //Calculate the area of the electrode given using the technique 479 | // 480 | float MiniStatAnalyst::calcArea(float charge) 481 | { 482 | return charge/CHARGE_TRANSFER_CONSTANT; 483 | } 484 | 485 | 486 | 487 | float MiniStatAnalyst::calcPhase(float freq, float dt, uint8_t scale) 488 | { 489 | return 360*freq*dt/pow(10,scale); 490 | } 491 | 492 | 493 | 494 | float MiniStatAnalyst::calcImg(float z, float theta) 495 | { 496 | return z*sin(theta*PI/180); 497 | } 498 | 499 | 500 | 501 | ////do i call it imp, z, mag 502 | ////do i call it phase, theta, angle 503 | float MiniStatAnalyst::calcReal(float z, float theta) 504 | { 505 | return z*cos(theta*PI/180); 506 | } 507 | 508 | 509 | //float MiniStatAnalyst::getMax() 510 | //data array containing data to be analyzed 511 | //samples number of elements in input array 512 | // 513 | //Determines maximum value of input data array. 514 | float MiniStatAnalyst::getMax(const float data[], uint16_t samples) 515 | { 516 | //assumes first value is the max, then checks if any 517 | //subsequent value in the array is bigger than the first 518 | float max = data[0]; 519 | 520 | for(uint16_t i = 1; i < samples; i++) 521 | { 522 | if(data[i] > max) max = data[i]; 523 | } 524 | 525 | return max; 526 | } 527 | 528 | 529 | //float MiniStatAnalyst::getMin() 530 | //data array containing data to be analyzed 531 | //samples number of elements in input array 532 | // 533 | //Determines minimum value of input data array. 534 | float MiniStatAnalyst::getMin(const float data[], uint16_t samples) 535 | { 536 | //assumes first value is the minimum, then checks if any subsequent value 537 | //in the array is smaller than the first 538 | float min = data[0]; 539 | 540 | for(uint16_t i = 1; i < samples; i++) 541 | { 542 | if(data[i] < min) min = data[i]; 543 | } 544 | 545 | return min; 546 | } 547 | 548 | 549 | //float MiniStatAnalyst::getPeaktoPeak() 550 | //data array containing data to be analyzed 551 | //samples number of elements in input array 552 | // 553 | //Calculates peak-to-peak value of AC signal in the "data" array. 554 | float MiniStatAnalyst::getPeaktoPeak(const float data[], uint16_t samples) 555 | { 556 | return getMax(data,samples) - getMin(data,samples); 557 | } 558 | 559 | 560 | //float MiniStatAnalyst::getPeaktoPeak() 561 | //data array containing data to be analyzed 562 | //samples number of elements in input array 563 | //max maximum value of input data array 564 | //min minimum value of input data array 565 | // 566 | //Calculates peak-to-peak value of AC signal in the "data" array. 567 | float MiniStatAnalyst::getPeaktoPeak(const float data[], uint16_t samples, 568 | float &max, float &min) 569 | { 570 | //assumes first value is the min/max, then checks if any subsequent value 571 | //in the array is smaller/bigger than the first 572 | max = data[0]; 573 | min = data[0]; 574 | 575 | for(uint16_t i = 1; i < samples; i++) 576 | { 577 | if(data[i] > max) max = data[i]; 578 | if(data[i] < min) min = data[i]; 579 | } 580 | 581 | return max-min; 582 | } 583 | 584 | 585 | //float MiniStatAnalyst::getAverage(const float data[], uint16_t samples) 586 | //data array containing data to average 587 | //samples number of samples in input array 588 | // 589 | //Returns average value of input array 590 | float MiniStatAnalyst::getAverage(const float data[], uint16_t samples) 591 | { 592 | float sum = 0; 593 | 594 | for(uint16_t i = 0; i < samples; i++) 595 | { 596 | sum += data[i]; 597 | } 598 | 599 | return sum/samples; 600 | } 601 | 602 | 603 | double MiniStatAnalyst::getZeroCrossing(const float data[], const unsigned long time[], 604 | const float avg, const uint16_t samples) 605 | { 606 | for(uint16_t i = 0; i < samples-1; i++) 607 | { 608 | if( data[i+1]-avg == 0 ) return time[i+1]; 609 | else if( data[i]-avg == 0 ) return time[i]; 610 | else if( (data[i+1]-avg)/(data[i]-avg) < 0) 611 | { 612 | SerialUSB.println(); 613 | SerialUSB.println(); 614 | SerialUSB.print(i); 615 | SerialUSB.println(); 616 | SerialUSB.println(); 617 | 618 | double slope = (data[i+1] - data[i]) / (time[i+1] - time[i]); 619 | double intercept = (data[i]-avg)-slope*time[i]; 620 | return (0-intercept)/slope; 621 | } 622 | } 623 | 624 | return 0; 625 | } 626 | 627 | 628 | 629 | //void MiniStatAnalyst::getWaveformStatistics(const float data[], uint16_t samples, 630 | // float &max, float &min, float &pkpk, 631 | // float &avg) 632 | //{ 633 | // float sum = 0; 634 | // 635 | // for(uint16_t i = 0; i < samples; i++) 636 | // { 637 | // sum += data[i]; 638 | // } 639 | // 640 | // avg = sum/samples; 641 | // pkpk = max-min; 642 | //} 643 | 644 | 645 | 646 | //float MiniStatAnalyst::calcCapacitance(float imgImp, float freq) 647 | //{ 648 | // return 1/(2*PI*freq*abs(imgImp)); 649 | //} 650 | 651 | 652 | 653 | /* 654 | HELPFUL LINKS 655 | 656 | polar and rectangular coordinates 657 | https://www.intmath.com/complex-numbers/convert-polar-rectangular-interactive.php 658 | https://www.mathsisfun.com/polar-cartesian-coordinates.html 659 | 660 | EIS 661 | https://www.gamry.com/application-notes/EIS/basics-of-electrochemical-impedance-spectroscopy/ 662 | 663 | Calculate phase angle from time delay 664 | http://www.sengpielaudio.com/calculator-timedelayphase.htm 665 | 666 | faster ADC on SAMD21 667 | https://forum.arduino.cc/index.php?topic=338640.0 668 | https://forum.arduino.cc/index.php?topic=341345.0 669 | Google Search "analog read low level samd21" 670 | 671 | 672 | */ 673 | 674 | 675 | 676 | 677 | 678 | 679 | ////This seems useful to know https://en.wikipedia.org/wiki/Linear_least_squares#Example, 680 | ////although this is really the guide this method is based on https://youtu.be/1C3olrs1CUw 681 | ////void MiniStatAnalyst::calcTangentLine(Queue current_copy, 682 | //// Queue voltage_copy, 683 | //// float &slope, float &intercept) 684 | //void MiniStatAnalyst::calcTangentLine(const float current, const float voltage, 685 | // float &slope, float &intercept) 686 | //{ 687 | // float samples = 0; 688 | // float volt = 0; 689 | // float amp = 0; 690 | // 691 | // float xy_sum = 0; 692 | // float x_sum = 0; 693 | // float y_sum = 0; 694 | // float x_squared_sum = 0; 695 | // 696 | // while(abs(volt) < 200 && !voltage_copy.isEmpty() && !current_copy.isEmpty()) 697 | // { 698 | // volt = voltage_copy.front(); 699 | // amp = current_copy.front(); 700 | // 701 | // xy_sum += volt*amp; 702 | // x_sum += volt; 703 | // y_sum += amp; 704 | // x_squared_sum += volt*volt; 705 | // 706 | // voltage_copy.dequeue(); 707 | // current_copy.dequeue(); 708 | // 709 | // samples++; 710 | // 711 | // SerialUSB.print("I'm in the calcTangentLine Method"); 712 | // SerialUSB.println(volt); 713 | // } 714 | // 715 | // 716 | // slope = (xy_sum - (x_sum*y_sum/samples))/(x_squared_sum - (x_sum*x_sum/samples)); 717 | // intercept = y_sum/samples - slope*x_sum/samples; 718 | //} 719 | // 720 | // 721 | ////float MiniStatAnalyst::getPeakCurrent(Queue current_copy, 722 | //// Queue voltage_copy, 723 | //// float slope, float intercept) 724 | //float MiniStatAnalyst::getPeakCurrent(float current, float voltage, 725 | // float slope, float intercept) 726 | //{ 727 | // float volt = voltage_copy.front(); 728 | // float amp = current_copy.front(); 729 | // 730 | // float peak = amp - slope*volt-intercept; 731 | // voltage_copy.dequeue(); 732 | // current_copy.dequeue(); 733 | // 734 | // 735 | // while(abs(volt) < 400 && !voltage_copy.isEmpty() && !current_copy.isEmpty()) 736 | // { 737 | // volt = voltage_copy.front(); 738 | // amp = current_copy.front(); 739 | // 740 | // if(abs(peak) < abs(amp - slope*volt-intercept)) 741 | // { 742 | // peak = amp - slope*volt-intercept; 743 | // 744 | // SerialUSB.print("volt: "); 745 | // SerialUSB.println(volt); 746 | // } 747 | // 748 | // voltage_copy.dequeue(); 749 | // current_copy.dequeue(); 750 | // } 751 | // 752 | // 753 | // return peak; 754 | //} 755 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/MiniStatAnalyst/MiniStatAnalyst.h: -------------------------------------------------------------------------------- 1 | /* 2 | FILENAME: MiniStatAnalyst.h 3 | AUTHOR: Orlando S. Hoilett 4 | EMAIL: ohoilett@purdue.edu 5 | 6 | 7 | Please see .cpp file for extended descriptions, instructions, and version updates 8 | 9 | 10 | DISCLAIMER 11 | Linnes Lab code, firmware, and software is released under the MIT License 12 | (http://opensource.org/licenses/MIT). 13 | 14 | The MIT License (MIT) 15 | 16 | Copyright (c) 2019 Linnes Lab, Purdue University, West Lafayette, IN, USA 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights to 21 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 22 | of the Software, and to permit persons to whom the Software is furnished to do 23 | so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all 26 | copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | 36 | */ 37 | 38 | 39 | #ifndef MiniStatAnalyst_h 40 | #define MiniStatAnalyst_h 41 | 42 | 43 | //Standard Arduino libraries 44 | #include 45 | #include 46 | #include 47 | 48 | 49 | const float AVOGADRO = 6.022*pow(10,23); 50 | const float FARADAY = 96485.33289; 51 | const float CHARGE_TRANSFER_CONSTANT = 400; //400uC/cm2 52 | 53 | const int8_t REDUCTION = -1; 54 | const int8_t OXIDATION = 1; 55 | 56 | 57 | //ERROR CODES 58 | const uint16_t NO_ERROR = 0x00; 59 | 60 | const uint16_t NO_REDOX_TYPE = 0x01; 61 | const uint16_t NO_BASELINE_SELECTED = 0x02; 62 | const uint16_t NO_BASELINE_FOUND = 0x03; 63 | const uint16_t NO_PEAK_REGION_SELECTED = 0x04; 64 | 65 | 66 | //Experiment Codes 67 | const uint8_t MB_APTAMER_BASE = 0; 68 | const uint8_t FeCN_OX_BASE = 1; 69 | const uint8_t FeCN_RED_BASE = 2; 70 | 71 | const uint8_t MB_APTAMER_PEAK = 3; 72 | const uint8_t FeCN_OX_PEAK = 4; 73 | const uint8_t FeCN_RED_PEAK = 5; 74 | 75 | 76 | 77 | class MiniStatAnalyst 78 | { 79 | 80 | private: 81 | 82 | void checkVBounds(int16_t &v1, int16_t &v2, const int8_t redox); 83 | void setBounds(const uint8_t &exp, int16_t &v1, int16_t &v2, int8_t &redox); 84 | 85 | 86 | public: 87 | 88 | MiniStatAnalyst(); //DEFAULT CONSTRUCTOR 89 | 90 | float calcDerivative(int16_t num1, int16_t num2, int16_t dt); 91 | 92 | //uint32_t calcResiduals(uint16_t samples); 93 | 94 | //void calcStDev(); 95 | 96 | uint16_t calcBaseline(int16_t v1, int16_t v2, int8_t redox, 97 | const float current[], const int16_t voltage[], 98 | float &slope, float &intercept, uint16_t samples); 99 | uint16_t calcBaseline(uint8_t exp, const float current[], 100 | const int16_t voltage[], float &slope, 101 | float &intercept, uint16_t samples); 102 | 103 | uint16_t getPeakCurrent(int16_t v1, int16_t v2, int8_t redox, 104 | const float current[], const int16_t voltage[], 105 | const float slope, const float intercept, 106 | float &peak, int16_t &v_peak, uint16_t samples); 107 | uint16_t getPeakCurrent(uint8_t exp, const float current[], 108 | const int16_t voltage[], const float slope, 109 | const float intercept, float &peak, 110 | int16_t &v_peak, uint16_t samples); 111 | 112 | 113 | float calcProbeDensity(float charge, uint8_t num_electrons, float area); 114 | float calcCharge(float current, int16_t voltage, uint16_t scan_rate); 115 | float calcArea(float charge); 116 | 117 | 118 | float calcPhase(float freq, float dt, uint8_t scale); 119 | float calcImg(float z, float theta); 120 | float calcReal(float z, float theta); 121 | 122 | float getMax(const float data[], uint16_t samples); 123 | float getMin(const float data[], uint16_t samples); 124 | float getPeaktoPeak(const float data[], uint16_t samples); 125 | float getPeaktoPeak(const float data[], uint16_t samples, float &max, float &min); 126 | 127 | float getAverage(const float data[], uint16_t samples); 128 | double getZeroCrossing(const float data[], const unsigned long time[], 129 | const float avg, const uint16_t samples); 130 | 131 | }; 132 | 133 | 134 | #endif /* MiniStatAnalyst_h */ 135 | 136 | 137 | 138 | /* 139 | 140 | 141 | Get all the data 142 | 143 | 144 | Find the baseline area 145 | - should be fitted with a line (so maybe find baseline by looking at first and 146 | second derivatives) 147 | 148 | 149 | Isolate the baseline area 150 | - either create a new array, or figure out which indices correspond to the 151 | baseline 152 | 153 | 154 | Find trendline 155 | - least squares method 156 | 157 | 158 | */ 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /KickStat/Firmware/libraries/MiniStatAnalyst/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for MiniStatAnalyst 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | MiniStatAnalyst KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | 16 | calcDerivative KEYWORD2 17 | calcBaseline KEYWORD2 18 | getPeakCurrent KEYWORD2 19 | calcProbeDensity KEYWORD2 20 | calcCharge KEYWORD2 21 | calcArea KEYWORD2 22 | 23 | ####################################### 24 | # Constants (LITERAL1) 25 | ####################################### 26 | 27 | 28 | AVOGADRO LITERAL1 29 | FARADAY LITERAL1 30 | CHARGE_TRANSFER_CONSTANT LITERAL1 31 | 32 | REDUCTION LITERAL1 33 | OXIDATION LITERAL1 34 | 35 | NO_ERROR LITERAL1 36 | NO_REDOX_TYPE LITERAL1 37 | NO_BASELINE_SELECTED LITERAL1 38 | NO_BASELINE_FOUND LITERAL1 39 | 40 | MB_APTAMER_BASE LITERAL1 41 | FeCN_OX_BASE LITERAL1 42 | FeCN_RED_BASE LITERAL1 43 | MB_APTAMER_PEAK LITERAL1 44 | FeCN_OX_PEAK LITERAL1 45 | FeCN_RED_PEAK LITERAL1 46 | 47 | 48 | -------------------------------------------------------------------------------- /KickStat/Firmware/references/README.md: -------------------------------------------------------------------------------- 1 | The contents of this folder have moved to [KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Bootloading-SAMD21.pdf](https://github.com/LinnesLab/KickStat-Paper-Firmware/blob/master/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Drivers-SparkFun-SAMD21.pdf) 2 | -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Assembly-Instructions.txt: -------------------------------------------------------------------------------- 1 | For clarity and organization, the contents of this file have been moved to the main README.md in the root directory. 2 | https://github.com/LinnesLab/KickStat-Paper-Firmware 3 | -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Bootloading-SAMD21.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Bootloading-SAMD21.pdf -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Drivers-SparkFun-SAMD21.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Drivers-SparkFun-SAMD21.pdf -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/KickStat-LED-Cathode-Mark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/KickStat-LED-Cathode-Mark.jpg -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/PCBWay-Ordering-Parameters-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/PCBWay-Ordering-Parameters-01.png -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/PCBWay-Ordering-Parameters-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/PCBWay-Ordering-Parameters-02.png -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/PCBWay-Ordering-Parameters-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/PCBWay-Ordering-Parameters-03.png -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Setting-up-Arduino-SparkFun-SAMD21.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Setting-up-Arduino-SparkFun-SAMD21.pdf -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-BOM.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-BOM.xlsx -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-RevB-Centroid-2019-07-29-1055.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-RevB-Centroid-2019-07-29-1055.zip -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-RevB-Gerbers-2019-07-29-1030.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-RevB-Gerbers-2019-07-29-1030.zip -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-brd.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-brd.pdf -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-sch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/MiniStat-Button-sch.pdf -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/images/IMG_2810.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/images/IMG_2810.JPG -------------------------------------------------------------------------------- /KickStat/Hardware/Button-Cell/RevB/images/IMG_2840.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinnesLab/KickStat-Paper-Firmware/4eb6b90acd8cdc9ebaacbb316bf5fd52988a8ebc/KickStat/Hardware/Button-Cell/RevB/images/IMG_2840.JPG -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KickStat-Paper-Firmware 2 | Firmware used to collect data for first KickStat publication "KickStat: A Coin-Sized Potentiostat for High-Resolution Electrochemical Analysis" doi: https://doi.org/10.3390/s20082407 3 | 4 | **Note:** KickStat was originally named MiniStat hence a few files still bearing the root name "MiniStat." We had to change the name after we found another publication with the name MiniStat, though earlier versions of our design (which was called MiniStat) were published on GitHub 3 years before the publication of the other manuscript in question. We apologize for the confusion. 5 | 6 | **Note:** KickStat uses the [SparkFun SAMD21 Dev Breakout](https://www.sparkfun.com/products/13672) board definition. Instructions for setting up your board as well as the Arduino IDE are below. 7 |

8 | 9 | ## Hardware Setup Instructions 10 | ### Ordering Boards from PCBWay 11 | 1. PCBWay parameters are located in attached images in the following location: 12 | [KickStat-Paper-Firmware/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions](https://github.com/LinnesLab/KickStat-Paper-Firmware/tree/master/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions) 13 | files: [PCBWay-Ordering-Parameters-01](https://github.com/LinnesLab/KickStat-Paper-Firmware/blob/master/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/PCBWay-Ordering-Parameters-01.png), [PCBWay-Ordering-Parameters-02](https://github.com/LinnesLab/KickStat-Paper-Firmware/blob/master/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/PCBWay-Ordering-Parameters-02.png), [PCBWay-Ordering-Parameters-03](https://github.com/LinnesLab/KickStat-Paper-Firmware/blob/master/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/PCBWay-Ordering-Parameters-03.png) 14 | 15 | 2. Boards can be fabricated and assembled by PCBWay. Go to PCBWay's website and click the PCB Assembly option. You can select how many assembled PCBs you would like. Feel free to leave unique number of parts, SMT pads, and thru-holes blank. PCBWay will populate those fields themselves. 16 | 17 | 3. Feel free to specify whatever surface finish you prefer. I used HASL with lead, but if you feel more comfortable with lead-free, HASL lead-free and Immersion gold (ENIG) are very economical options. 18 | 19 | 4. Upload the Gerbers, centroid file, and the BOM to PCB Way's quote system. All those files are located in the [Assembly Instructions folder](https://github.com/LinnesLab/KickStat-Paper-Firmware/tree/master/KickStat/Hardware/Button-Cell/RevB). It will take 2-4 days to review your design. They will contact you over email when the review is complete or if they have any questions. 20 | 21 | 5. **Note:** PCBWay often asks about the cathode location for the LEDs. The cathode locations are as noted in the ["KickStat-LED-Cathode-Mark.jpg"](https://github.com/LinnesLab/KickStat-Paper-Firmware/blob/master/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/KickStat-LED-Cathode-Mark.jpg) file. You can simply send them this file in an email. Be sure to reference your Product No. given in their quote system. 22 | 23 | 6. **Note:** I like to solder the SWD programming header (10-pin connector) myself (it's located on the bottom of the board), but PCBWay could also solder the headers as well. You'll just need to specify Assembly Side(s): as "Both sides." It might cost a little extra, but still very inexpensive as far as PCB assembly goes. You will also need to give them the part number for the SWD 10-pin connector. I like these options: from [Adafruit PID: 752](https://www.adafruit.com/product/752), from [Digi-Key Part Number: 1175-1735-ND, Manufacturer Part Number: 3221-10-0300-00](https://www.digikey.com/products/en?keywords=1175-1735-ND), as well as [Digi-Key Part Number: 609-3695-1-ND, Manufacturer Part Number: 20021121-00010C4LF](https://www.digikey.com/products/en?keywords=609-3695-1-ND) 24 | 25 | 26 | ## Firmware Setup Instructions 27 | 1. Bootload your SAMD21 with the SparkFun SAMD21 Dev Board definition using these instructions ["Bootloading-SAMD21.pdf"](https://github.com/LinnesLab/KickStat-Paper-Firmware/blob/master/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Bootloading-SAMD21.pdf). You will need an SWD 10-pin connector (as discussed in the Hardware Setup Instructions section) and a [J-Link](https://www.adafruit.com/product/1369) 28 | 29 | 2. Download [SparkFun SAMD21 Drivers](https://github.com/LinnesLab/KickStat-Paper-Firmware/blob/master/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Drivers-SparkFun-SAMD21.pdf) 30 | 31 | 3. [Set up Arduino for SparkFun SAMD21 Board Definition](https://github.com/LinnesLab/KickStat-Paper-Firmware/blob/master/KickStat/Hardware/Button-Cell/RevB/Assembly-Instructions/Setting-up-Arduino-SparkFun-SAMD21.pdf). **Note:** I am using Arduino v.1.8.12, Arduino SAMD Boards v.1.8.6, and SparkFun SAMD Board v.1.7.5. I would recommend using all the same versions to avoid any trouble. 32 | 33 | 5. Download contents of the Firmware/libraries folder [(KickStat/Firmware/libraries)](https://github.com/LinnesLab/KickStat-Paper-Firmware/tree/master/KickStat/Firmware/libraries) into the Arduino libraries folder often located in: documents/arduino of your computer. But always double check. The contents of libraries include the [MiniStatAnalyst library](https://github.com/LinnesLab/KickStat-Paper-Firmware/tree/master/KickStat/Firmware/libraries/MiniStatAnalyst) and the [LMP91000 library](https://github.com/LinnesLab/KickStat-Paper-Firmware/tree/master/KickStat/Firmware/libraries/LMP91000). Both are necessary to run KickStat. Instructions for downloading Arduino libraries can be found on [Arduino's website](https://www.arduino.cc/en/guide/libraries) 34 | 35 | 6. Once all that is complete, modify the code as you wish, select the proper board definition within the Arduino IDE (SparkFun SAMD21 Dev Breakout), select the proper port, and you can upload. 36 | --------------------------------------------------------------------------------