main.cpp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415
  1. #define NEXTION_SUPPORT
  2. #define PLATFORM_IO
  3. // CurrentRanger(TM) stock firmware
  4. // https://lowpowerlab.com/CurrentRanger
  5. // CurrentRanger is a *high-side* precision current meter featuring:
  6. // - fast autoranging
  7. // - uni/bi-directional modes (ie. DC/AC measurements)
  8. // - ultra low burden voltage
  9. // - 1mV per nA/uA/mA measurements with DMM/scope
  10. // - OLED standalone readings
  11. // - serial data logging option via 3.3v/RX/TX header or USB (must use isolation, read guide!)
  12. // - full digital control for power/switching
  13. // - LiPo powered with auto power-off feature (0.6uA quiescent current)
  14. // *************************************************************************************************************
  15. #ifndef CURRENT_RANGER_NEXTION
  16. //#error CurrentRanger target board required, see guide on how to add it to the IDE: lowpowerlab.com/currentranger
  17. #endif
  18. //***********************************************************************************************************
  19. #include <FlashStorage.h> //for emulated EEPROM - https://github.com/cmaglie/FlashStorage
  20. #include <Adafruit_FreeTouch.h> //https://github.com/adafruit/Adafruit_FreeTouch
  21. #ifdef OLED_SUPPORT
  22. #include <U8g2lib.h> //https://github.com/olikraus/u8g2/wiki/u8g2reference fonts:https://github.com/olikraus/u8g2/wiki/fntlistall
  23. #endif
  24. //#include <ATSAMD21_ADC.h>
  25. #ifdef NEXTION_SUPPORT
  26. #include "EasyNextionLibrary.h"
  27. #endif
  28. // CurrentRanger Firmware Version
  29. #define FW_VERSION "1.1.0c"
  30. //***********************************************************************************************************
  31. #define BIAS_LED 11
  32. #define LPFPIN 4
  33. #define LPFLED LED_BUILTIN
  34. #define AUTOFF PIN_AUTO_OFF
  35. //***********************************************************************************************************
  36. #define MA_PIN PIN_PA13 //#define MA 38
  37. #define UA_PIN PIN_PA14 //#define UA 2
  38. #define NA_PIN PIN_PA15 //#define NA 5
  39. #define MA_GPIO_PIN PIN_PB11
  40. #define UA_GPIO_PIN PIN_PA12
  41. #define NA_GPIO_PIN PIN_PB10
  42. #define PINOP(pin, OP) (PORT->Group[(pin) / 32].OP.reg = (1 << ((pin) % 32)))
  43. #define PIN_OFF(THE_PIN) PINOP(THE_PIN, OUTCLR)
  44. #define PIN_ON(THE_PIN) PINOP(THE_PIN, OUTSET)
  45. #define PIN_TGL(THE_PIN) PINOP(THE_PIN, OUTTGL)
  46. //***********************************************************************************************************
  47. #define SENSE_OUTPUT A3
  48. #define SENSE_GNDISO A2
  49. #define SENSE_VIN A5
  50. #define ADC_PRESCALER ADC_CTRLB_PRESCALER_DIV16
  51. //#define ADC_AVGCTRL ADC_AVGCTRL_SAMPLENUM_128 | ADC_AVGCTRL_ADJRES(0x4ul)
  52. //ADC_AVGCTRL_SAMPLENUM_1 | ADC_AVGCTRL_ADJRES(0x00ul); // take 1 sample, adjusting result by 0
  53. //ADC_AVGCTRL_SAMPLENUM_16 | ADC_AVGCTRL_ADJRES(0x4ul); //take 16 samples adjust by 4
  54. //ADC_AVGCTRL_SAMPLENUM_256 | ADC_AVGCTRL_ADJRES(0x4ul); //take 256 samples adjust by 4
  55. //ADC_AVGCTRL_SAMPLENUM_512 | ADC_AVGCTRL_ADJRES(0x4ul); //take 512 samples adjust by 4
  56. //ADC_AVGCTRL_SAMPLENUM_1024 | ADC_AVGCTRL_ADJRES(0x4ul); //take 1024 samples adjust by 4
  57. #define ADC_SAMPCTRL 0b111 //sample timing [fast 0..0b111 slow]
  58. #define ADCFULLRANGE 4095.0
  59. #define VBAT_REFRESH_INTERVAL 5000 //ms
  60. #define LOBAT_THRESHOLD 3.40 //volts
  61. #define DAC_GND_ISO_OFFSET 10
  62. #define DAC_HALF_SUPPLY_OFFSET 512
  63. #define OUTPUT_CALIB_FACTOR 1.00 //calibrate final VOUT value
  64. #define ADC_OVERLOAD 3900 //assuming GNDISO DAC output is very close to 0, this is max value less ground offset (varies from unit to unit, 3900 is a safe value)
  65. //***********************************************************************************************************
  66. //#define ADC_CALIBRATE_FORCED
  67. #define ADC_CALIBRATE_FORCED_OFFSET 0
  68. #define ADC_CALIBRATE_FORCED_GAIN 2048
  69. #define LDO_DEFAULT 3.300 //volts, change to actual LDO output (measure GND-3V on OLED header)
  70. //***********************************************************************************************************
  71. #define BUZZER 1 // BUZZER pin
  72. #define NOTE_C5 523
  73. #define NOTE_D5 587
  74. #define NOTE_E5 659
  75. #define NOTE_F5 698
  76. #define NOTE_G5 784
  77. #define NOTE_B5 988
  78. #define NOTE_C6 1047
  79. #define TONE_BEEP 4200
  80. //***********************************************************************************************************
  81. #define MODE_MANUAL 0
  82. #define MODE_AUTORANGE 1
  83. #define STARTUP_MODE MODE_MANUAL //or: MODE_AUTORANGE
  84. #define SWITCHDELAY_UP 8 //ms
  85. #define SWITCHDELAY_DOWN 8 //ms
  86. #define RANGE_SWITCH_THRESHOLD_HIGH ADC_OVERLOAD //ADC's 12bit value
  87. #define RANGE_SWITCH_THRESHOLD_LOW 6 //6*0.4xA ~ 2.4xA - range down below this value
  88. //***********************************************************************************************************
  89. #ifdef OLED_SUPPORT
  90. #include <Wire.h> //i2c scanner: https://playground.arduino.cc/Main/I2cScanner
  91. #define OLED_BAUD 1600000 //fast i2c clock
  92. #define OLED_ADDRESS 0x3C //i2c address on most small OLEDs
  93. #define OLED_REFRESH_INTERVAL 180 //ms
  94. U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
  95. #endif
  96. //***********************************************************************************************************
  97. #define TOUCH_N 8
  98. #define TOUCH_U 9
  99. #define TOUCH_M A4
  100. Adafruit_FreeTouch qt[3] = {
  101. Adafruit_FreeTouch( TOUCH_N, OVERSAMPLE_1, RESISTOR_50K, FREQ_MODE_NONE ),
  102. Adafruit_FreeTouch( TOUCH_U, OVERSAMPLE_1, RESISTOR_50K, FREQ_MODE_NONE ),
  103. Adafruit_FreeTouch( TOUCH_M, OVERSAMPLE_1, RESISTOR_50K, FREQ_MODE_NONE ),
  104. };
  105. #define TOUCH_HIGH_THRESHOLD 400 //range is 0..1023
  106. #define TOUCH_SAMPLE_INTERVAL 50 //ms
  107. //***********************************************************************************************************
  108. #define SERIAL_UART_BAUD 230400 //Serial baud for HC-06/bluetooth output
  109. #define BT_SERIAL_EN
  110. //#define LOGGER_FORMAT_EXPONENT //ex: 123E-3 = 123mA
  111. //#define LOGGER_FORMAT_NANOS //ex: 123456 = 123456nA = 123.456uA
  112. //#define LOGGER_FORMAT_ADC //raw ADC output - note: automatic ADC_REF change
  113. #define BT_REFRESH_INTERVAL 200 //ms
  114. //***********************************************************************************************************
  115. #define AUTOOFF_BUZZ_DELAY 500 //ms
  116. #define AUTOOFF_DEFAULT 600 //seconds, turn unit off after 10min of inactivity
  117. #define AUTOOFF_DISABLED 0xFFFF // do not turn off
  118. #define AUTOOFF_SMART 0xFFFE // turn off only if there is no BT or USB data logging
  119. //***********************************************************************************************************
  120. #define LOGGING_FORMAT_EXPONENT 0 //ex: 123E-3 = 123mA
  121. #define LOGGING_FORMAT_NANOS 1 //ex: 1234 = 1.234uA = 0.001234mA
  122. #define LOGGING_FORMAT_MICROS 2 //ex: 1234 = 1.234mA = 1234000nA
  123. #define LOGGING_FORMAT_MILLIS 3 //ex: 1234 = 1.234A = 1234000uA = 1234000000nA
  124. #define LOGGING_FORMAT_ADC 4 //raw output for each range (0..4095)
  125. //***********************************************************************************************************
  126. #define ADC_SAMPLING_SPEED_AVG 0
  127. #define ADC_SAMPLING_SPEED_FAST 1
  128. #define ADC_SAMPLING_SPEED_SLOW 2
  129. //***********************************************************************************************************
  130. #ifdef NEXTION_SUPPORT
  131. #define NEX_LIGHT_GREY 50712
  132. #define NEX_BLUE 1055
  133. #define NEXTION_REFRESH_INTERVAL 300 //ms
  134. EasyNex myNex(SerialNextion);
  135. uint8_t nex_page=0;
  136. bool nex_timer_run=false;
  137. bool nex_timerstats_run=false;
  138. uint8_t nex_timer_range = 0;
  139. uint8_t nex_timer_d0=0;
  140. uint8_t nex_timer_d1=0;
  141. uint8_t nex_timer_d2=0;
  142. uint8_t nex_timer_d3=0;
  143. uint8_t nex_timer_set=0;
  144. uint8_t nex_timer_output=0;
  145. uint32_t nex_timer_start=0;
  146. uint32_t nex_timer_stop=0;
  147. String nex_timer_current="";
  148. bool nex_timer_trigger=false;
  149. float nex_stats_min = 0;
  150. float nex_stats_avg = 0;
  151. uint32_t nex_stats_count=0;
  152. float nex_stats_max = 0;
  153. String nex_stats_max_s = "";
  154. String nex_stats_avg_s = "";
  155. String nex_stats_min_s = "";
  156. #endif
  157. int offsetCorrectionValue = 0;
  158. uint16_t gainCorrectionValue = 0;
  159. float ldoValue = 0, ldoOptimized=0;
  160. uint16_t autooff_interval = 0;
  161. uint8_t USB_LOGGING_ENABLED = false;
  162. uint8_t TOUCH_DEBUG_ENABLED = false;
  163. uint8_t GPIO_HEADER_RANGING = false;
  164. uint8_t BT_LOGGING_ENABLED = true;
  165. uint8_t LOGGING_FORMAT = LOGGING_FORMAT_EXPONENT;
  166. uint16_t ADC_SAMPLING_SPEED = ADC_SAMPLING_SPEED_AVG;
  167. uint32_t ADC_AVGCTRL;
  168. uint8_t calibrationPerformed=false;
  169. uint8_t analog_ref_half=true;
  170. char rangeUnit = 'm';
  171. uint8_t OLED_found=false;
  172. uint8_t autoffWarning=false;
  173. uint8_t autoffBuzz=0;
  174. #ifdef BT_SERIAL_EN
  175. uint8_t BT_found=false;
  176. #endif
  177. FlashStorage(eeprom_ADCoffset, int);
  178. FlashStorage(eeprom_ADCgain, uint16_t);
  179. FlashStorage(eeprom_LDO, float);
  180. FlashStorage(eeprom_AUTOFF, uint16_t);
  181. FlashStorage(eeprom_LOGGINGFORMAT, uint8_t);
  182. FlashStorage(eeprom_ADCSAMPLINGSPEED, uint8_t);
  183. //***********************************************************************************************************
  184. #ifdef PLATFORM_IO
  185. void WDTclear();
  186. void analogReadCorrection(int offset, uint16_t gain);
  187. void analogReferenceHalf(uint8_t half);
  188. void toggleAutoranging();
  189. void Beep(byte theDelay, boolean twoSounds);
  190. void toggleOffset();
  191. void rangeMA();
  192. void rangeUA();
  193. void rangeNA();
  194. void toggleLPF();
  195. void rangeBeep(uint16_t switch_delay);
  196. void handleTouchPads();
  197. void handleAutoOff();
  198. int adcRead(byte ADCpin);
  199. void handleVbatRead();
  200. void readVOUT();
  201. void printSerialMenu();
  202. void refreshADCSamplingSpeed();
  203. void rebootIntoBootloader();
  204. void saveLDO(float newLdoValue);
  205. void analogReadCorrectionForced(int offset, uint16_t gain);
  206. void WDTset();
  207. void ldoOptimizeRefresh();
  208. void chronometer();
  209. float vbat=0, VOUT=0;
  210. #endif
  211. //***********************************************************************************************************
  212. void setup() {
  213. /*
  214. //some buzz
  215. tone(BUZZER, NOTE_C5); delay(100);
  216. tone(BUZZER, NOTE_E5); delay(100);
  217. tone(BUZZER, NOTE_G5); delay(100);
  218. tone(BUZZER, NOTE_C6); delay(200);
  219. noTone(BUZZER); delay(50);
  220. tone(BUZZER, NOTE_G5); delay(100);
  221. tone(BUZZER, NOTE_C6); delay(400);
  222. noTone(BUZZER);
  223. */
  224. #ifdef NEXTION_SUPPORT
  225. myNex.begin(115200);
  226. #endif
  227. delay(50); //Wire apparently needs this
  228. #ifdef OLED_SUPPORT
  229. Wire.begin();
  230. Wire.beginTransmission(OLED_ADDRESS);
  231. byte error = Wire.endTransmission();
  232. if (error == 0)
  233. {
  234. Serial.print("OLED FOUND at 0x"); Serial.println(OLED_ADDRESS);
  235. u8g2.begin();
  236. //u8g2.setDisplayRotation(U8G2_R2); //if required (inside/custom mount?)
  237. u8g2.setBusClock(OLED_BAUD);
  238. OLED_found = true;
  239. }
  240. else Serial.println("NO OLED found...");
  241. #endif
  242. pinMode(A0, OUTPUT); //DAC/GNDISO
  243. //DAC->CTRLB.bit.EOEN = 0x00; //enable high drive strength - already done in wiring.c
  244. pinMode(SENSE_OUTPUT, INPUT);
  245. pinMode(SENSE_GNDISO, INPUT); //GND-ISO
  246. pinMode(SENSE_VIN, INPUT); //VIN > 1MEG > SENSE_VIN > 2MEG > GND
  247. pinMode(AUTOFF, INPUT_PULLUP);
  248. pinMode(BIAS_LED, OUTPUT);
  249. pinMode(LPFLED, OUTPUT); //STATUS/LPF-LED
  250. pinMode(LPFPIN, OUTPUT); //LPF control pin
  251. pinMode(BUZZER, OUTPUT);
  252. PINOP(MA_PIN, DIRSET);
  253. PINOP(UA_PIN, DIRSET);
  254. PINOP(NA_PIN, DIRSET);
  255. PINOP(MA_GPIO_PIN, DIRSET);
  256. PINOP(UA_GPIO_PIN, DIRSET);
  257. PINOP(NA_GPIO_PIN, DIRSET);
  258. qt[0].begin(); qt[1].begin(); qt[2].begin(); //touch pads
  259. analogWriteResolution(10); //DAC resolution
  260. analogReferenceHalf(true);
  261. //DAC->CTRLA.bit.RUNSTDBY = 0x01;delay(1);
  262. //DAC->CTRLB.bit.REFSEL=0;//pick internal reference, skip SYNCDAC (done by analogWrite)
  263. analogWrite(A0, DAC_GND_ISO_OFFSET); // Initialize Dac to OFFSET
  264. autooff_interval = eeprom_AUTOFF.read();
  265. if (autooff_interval==0) {
  266. autooff_interval = AUTOOFF_DEFAULT;
  267. eeprom_AUTOFF.write(autooff_interval);
  268. }
  269. LOGGING_FORMAT = eeprom_LOGGINGFORMAT.read();
  270. offsetCorrectionValue = eeprom_ADCoffset.read();
  271. gainCorrectionValue = eeprom_ADCgain.read();
  272. ldoValue = eeprom_LDO.read();
  273. if(ldoValue==0)
  274. saveLDO(LDO_DEFAULT);
  275. else ldoOptimizeRefresh();
  276. ADC_SAMPLING_SPEED = eeprom_ADCSAMPLINGSPEED.read();
  277. refreshADCSamplingSpeed(); //load correct value into ADC_AVGCTRL
  278. if (gainCorrectionValue!=0) //check if anything saved in EEPROM (gain changed via SerialUSB +/-)
  279. analogReadCorrectionForced(offsetCorrectionValue, gainCorrectionValue);
  280. else {
  281. analogReadCorrectionForced(ADC_CALIBRATE_FORCED_OFFSET, ADC_CALIBRATE_FORCED_GAIN);
  282. eeprom_ADCoffset.write(offsetCorrectionValue);
  283. eeprom_ADCgain.write(gainCorrectionValue);
  284. //(offset, gain) - gain is 12 bit number (1 bit integer + 11bit fractional, see DS p895)
  285. // - offset is 12bit 2s complement format (DS p896)
  286. }
  287. #ifdef OLED_SUPPORT
  288. if (OLED_found)
  289. {
  290. u8g2.clearBuffer();
  291. u8g2.setFont(u8g2_font_8x13B_tf);
  292. u8g2.setCursor(15,10); u8g2.print("CurrentRanger");
  293. u8g2.setFont(u8g2_font_6x12_tf);
  294. u8g2.setCursor(0,20); u8g2.print("Offset:");
  295. u8g2.setCursor(64,20); u8g2.print(offsetCorrectionValue);
  296. u8g2.setCursor(0,32); u8g2.print("Gain :");
  297. u8g2.setCursor(64,32); u8g2.print(gainCorrectionValue);
  298. u8g2.setCursor(0,44); u8g2.print("LDO :");
  299. u8g2.setCursor(64,44); u8g2.print(ldoValue,3);
  300. u8g2.setCursor(0, 56); u8g2.print("Firmware:");
  301. u8g2.setCursor(64,56); u8g2.print(FW_VERSION);
  302. u8g2.sendBuffer();
  303. delay(2000);
  304. }
  305. #endif
  306. #ifdef NEXTION_SUPPORT
  307. if (analog_ref_half)
  308. {
  309. analogReferenceHalf(false);
  310. vbat=adcRead(SENSE_VIN);
  311. analogReferenceHalf(true);
  312. }
  313. else vbat=adcRead(SENSE_VIN);
  314. vbat=((vbat/ADCFULLRANGE) * ldoValue) * 1.5; //1.5 given by vbat->A5 resistor ratio (1 / (2M * 1/(1M+2M)))
  315. myNex.writeStr("page page4","cmd");
  316. delay(2000);
  317. nex_page=2;
  318. myNex.writeStr("page page2","cmd");
  319. myNex.writeStr("offset.txt",(String)offsetCorrectionValue);
  320. myNex.writeStr("gain.txt",(String)gainCorrectionValue);
  321. myNex.writeStr("ldo.txt",String(ldoValue,3));
  322. myNex.writeStr("fw.txt",(String)FW_VERSION);
  323. myNex.writeStr("bat.txt",(String)vbat);
  324. delay(2000);
  325. myNex.writeStr("page page0","cmd");
  326. myNex.writeNum("b2.bco",NEX_BLUE);
  327. nex_page=0;
  328. #endif
  329. #ifdef BT_SERIAL_EN
  330. //BT check
  331. Serial.print("Bluetooth AT check @");Serial.print(SERIAL_UART_BAUD);Serial.print("baud...");
  332. delay(600);
  333. SerialBT.begin(SERIAL_UART_BAUD);
  334. SerialBT.print("AT"); //assuming HC-06, no line ending required
  335. uint32_t timer=millis();
  336. while(millis()-timer<1000) //about 1s to respond
  337. {
  338. if (SerialBT.available()==2 && SerialBT.read()=='O' && SerialBT.read()=='K')
  339. {
  340. BT_found=true;
  341. break;
  342. }
  343. }
  344. Serial.print(BT_found?"OK!":"No HC-06 response.\r\nChecking for BT v3.0...");
  345. if (!BT_found)
  346. {
  347. SerialBT.print("\r\n"); //assuming HC-06 version 3.0 that requires line ending
  348. uint32_t timer=millis();
  349. while(millis()-timer<50) //about 50ms to respond
  350. {
  351. if (SerialBT.available()==4 && SerialBT.read()=='O' && SerialBT.read()=='K' && SerialBT.read()=='\r' && SerialBT.read() == '\n')
  352. {
  353. BT_found=true;
  354. break;
  355. }
  356. }
  357. Serial.println(BT_found?"OK!":"No response.");
  358. }
  359. BT_LOGGING_ENABLED = BT_found;
  360. #endif
  361. printSerialMenu();
  362. WDTset();
  363. if (STARTUP_MODE == MODE_AUTORANGE) toggleAutoranging();
  364. }
  365. uint32_t oledInterval=0, lpfInterval=0, offsetInterval=0, autorangeInterval=0, btInterval=0,
  366. autoOffBuzzInterval=0, touchSampleInterval=0, lastKeepAlive=0, vbatInterval = VBAT_REFRESH_INTERVAL;
  367. #ifdef NEXTION_SUPPORT
  368. uint32_t nextionInterval=0;
  369. uint32_t nextionIntervalwrite=0;
  370. #endif
  371. byte LPF=0, BIAS=0, AUTORANGE=0;
  372. #ifndef PLATFORMIO
  373. float vbat=0, VOUT=0;
  374. #endif
  375. float read1=0,read2=0,readDiff=0;
  376. bool rangeSwitched=false;
  377. #define RANGE_MA rangeUnit=='m'
  378. #define RANGE_UA rangeUnit=='u'
  379. #define RANGE_NA rangeUnit=='n'
  380. void loop() {
  381. //uint32_t timestamp=micros();
  382. #ifdef NEXTION_SUPPORT
  383. if ( millis() - nextionInterval > NEXTION_REFRESH_INTERVAL) //refresh rate (ms)
  384. {
  385. myNex.NextionListen();
  386. nextionInterval = millis();
  387. }
  388. #endif
  389. while (Serial.available()>0) {
  390. char inByte = Serial.read();
  391. // tickle the AUTOOFF function so it doesn't shut down when there are commands coming over serial
  392. lastKeepAlive = millis();
  393. switch (inByte) {
  394. case '*':
  395. eeprom_ADCgain.write(++gainCorrectionValue);
  396. analogReadCorrection(offsetCorrectionValue,gainCorrectionValue);
  397. Serial.print("new gainCorrectionValue = ");
  398. Serial.println(gainCorrectionValue);
  399. break;
  400. case '/':
  401. eeprom_ADCgain.write(--gainCorrectionValue);
  402. analogReadCorrection(offsetCorrectionValue,gainCorrectionValue);
  403. Serial.print("new gainCorrectionValue = ");
  404. Serial.println(gainCorrectionValue);
  405. break;
  406. case '+':
  407. eeprom_ADCoffset.write(++offsetCorrectionValue);
  408. analogReadCorrection(offsetCorrectionValue,gainCorrectionValue);
  409. Serial.print("new offsetCorrectionValue = ");
  410. Serial.println(offsetCorrectionValue);
  411. break;
  412. case '-':
  413. eeprom_ADCoffset.write(--offsetCorrectionValue);
  414. analogReadCorrection(offsetCorrectionValue,gainCorrectionValue);
  415. Serial.print("new offsetCorrectionValue = ");
  416. Serial.println(offsetCorrectionValue);
  417. break;
  418. case '<':
  419. saveLDO(ldoValue-0.001);
  420. Serial.print("new LDO_Value = ");
  421. Serial.println(ldoValue, 3);
  422. break;
  423. case '>':
  424. saveLDO(ldoValue+0.001);
  425. Serial.print("new LDO_Value = ");
  426. Serial.println(ldoValue, 3);
  427. break;
  428. case 'r': //reboot to bootloader
  429. Serial.print("\nRebooting to bootloader.");
  430. for (byte i=0;i++<30;) { delay(10); Serial.print('.'); }
  431. rebootIntoBootloader();
  432. break;
  433. case 'u': //toggle USB logging
  434. USB_LOGGING_ENABLED =! USB_LOGGING_ENABLED;
  435. Serial.println(USB_LOGGING_ENABLED ? "USB_LOGGING_ENABLED" : "USB_LOGGING_DISABLED");
  436. #ifdef NEXTION_SUPPORT
  437. if (USB_LOGGING_ENABLED) myNex.writeNum("usb.pic",10);
  438. else myNex.writeNum("usb.pic",16);
  439. #endif
  440. break;
  441. case 't': //toggle touchpad serial output debug info
  442. TOUCH_DEBUG_ENABLED =! TOUCH_DEBUG_ENABLED;
  443. Serial.println(TOUCH_DEBUG_ENABLED ? "TOUCH_DEBUG_ENABLED" : "TOUCH_DEBUG_DISABLED");
  444. break;
  445. case 'g': //toggle GPIOs indicating ranging
  446. GPIO_HEADER_RANGING =! GPIO_HEADER_RANGING;
  447. if (GPIO_HEADER_RANGING) {
  448. if (rangeUnit=='m') PIN_ON(MA_GPIO_PIN); else PIN_OFF(MA_GPIO_PIN);
  449. if (rangeUnit=='u') PIN_ON(UA_GPIO_PIN); else PIN_OFF(UA_GPIO_PIN);
  450. if (rangeUnit=='n') PIN_ON(NA_GPIO_PIN); else PIN_OFF(NA_GPIO_PIN);
  451. }
  452. Serial.println(GPIO_HEADER_RANGING ? "GPIO_HEADER_RANGING_ENABLED" : "GPIO_HEADER_RANGING_DISABLED");
  453. break;
  454. case 'b': //toggle BT/serial logging
  455. #ifdef BT_SERIAL_EN
  456. if (BT_found) {
  457. BT_LOGGING_ENABLED =! BT_LOGGING_ENABLED;
  458. Serial.println(BT_LOGGING_ENABLED ? "BT_LOGGING_ENABLED" : "BT_LOGGING_DISABLED");
  459. } else {
  460. BT_LOGGING_ENABLED = false;
  461. Serial.println("BT Module not found: cannot enable logging");
  462. }
  463. #else
  464. Serial.println("BT_LOGGING Not Enabled");
  465. #endif
  466. break;
  467. case 'f': //cycle through output logging formats
  468. if (++LOGGING_FORMAT>LOGGING_FORMAT_ADC) LOGGING_FORMAT=LOGGING_FORMAT_EXPONENT;
  469. eeprom_LOGGINGFORMAT.write(LOGGING_FORMAT);
  470. if (LOGGING_FORMAT==LOGGING_FORMAT_EXPONENT) Serial.println("LOGGING_FORMAT_EXPONENT"); else
  471. if (LOGGING_FORMAT==LOGGING_FORMAT_NANOS) Serial.println("LOGGING_FORMAT_NANOS"); else
  472. if (LOGGING_FORMAT==LOGGING_FORMAT_MICROS) Serial.println("LOGGING_FORMAT_MICROS"); else
  473. if (LOGGING_FORMAT==LOGGING_FORMAT_MILLIS) Serial.println("LOGGING_FORMAT_MILLIS"); else
  474. if (LOGGING_FORMAT==LOGGING_FORMAT_ADC) Serial.println("LOGGING_FORMAT_ADC");
  475. break;
  476. case 's':
  477. if (++ADC_SAMPLING_SPEED>ADC_SAMPLING_SPEED_SLOW) ADC_SAMPLING_SPEED=ADC_SAMPLING_SPEED_AVG;
  478. if (ADC_SAMPLING_SPEED==ADC_SAMPLING_SPEED_AVG) Serial.println("ADC_SAMPLING_SPEED_AVG"); else
  479. if (ADC_SAMPLING_SPEED==ADC_SAMPLING_SPEED_FAST) Serial.println("ADC_SAMPLING_SPEED_FAST"); else
  480. if (ADC_SAMPLING_SPEED==ADC_SAMPLING_SPEED_SLOW) Serial.println("ADC_SAMPLING_SPEED_SLOW");
  481. eeprom_ADCSAMPLINGSPEED.write(ADC_SAMPLING_SPEED);
  482. refreshADCSamplingSpeed();
  483. break;
  484. case 'a': //toggle autoOff function
  485. if (autooff_interval == AUTOOFF_DEFAULT)
  486. {
  487. Serial.println("AUTOOFF_DISABLED");
  488. autooff_interval = AUTOOFF_DISABLED;
  489. }
  490. else if (autooff_interval == AUTOOFF_SMART) {
  491. Serial.println("AUTOOFF_DEFAULT");
  492. autooff_interval = AUTOOFF_DEFAULT;
  493. lastKeepAlive = millis();
  494. } else {
  495. // turn off only when there is no serial or BT data logging
  496. Serial.println("AUTOOFF_SMART");
  497. autooff_interval = AUTOOFF_SMART;
  498. }
  499. eeprom_AUTOFF.write(autooff_interval);
  500. break;
  501. case '?':
  502. printSerialMenu();
  503. break;
  504. default: break;
  505. }
  506. }
  507. if (AUTORANGE) {
  508. readVOUT();
  509. //assumes we only auto-range in DC mode (no bias)
  510. if (readDiff <= RANGE_SWITCH_THRESHOLD_LOW)
  511. {
  512. if (RANGE_MA) { rangeUA(); rangeSwitched=true; rangeBeep(SWITCHDELAY_DOWN); }
  513. else if (RANGE_UA) { rangeNA(); rangeSwitched=true; rangeBeep(SWITCHDELAY_DOWN); }
  514. }
  515. else if (readDiff >= RANGE_SWITCH_THRESHOLD_HIGH)
  516. {
  517. if (RANGE_NA) { rangeUA(); rangeSwitched=true; rangeBeep(SWITCHDELAY_UP); }
  518. else if (RANGE_UA) { rangeMA(); rangeSwitched=true; rangeBeep(SWITCHDELAY_UP); }
  519. }
  520. if (rangeSwitched) {
  521. lastKeepAlive=millis();
  522. rangeSwitched=false;
  523. return; //!!!
  524. }
  525. }
  526. uint8_t VOUTCalculated=false;
  527. if (USB_LOGGING_ENABLED)
  528. {//TODO: refactor
  529. if (!AUTORANGE) readVOUT();
  530. VOUT = readDiff*ldoOptimized*(BIAS?1:OUTPUT_CALIB_FACTOR);
  531. VOUTCalculated=true;
  532. if(LOGGING_FORMAT == LOGGING_FORMAT_EXPONENT) { Serial.print(VOUT); Serial.print("e"); Serial.println(RANGE_NA ? -9 : RANGE_UA ? -6 : -3); } else
  533. if(LOGGING_FORMAT == LOGGING_FORMAT_NANOS) Serial.println(VOUT * (RANGE_NA ? 1 : RANGE_UA ? 1000 : 1000000)); else
  534. if(LOGGING_FORMAT == LOGGING_FORMAT_MICROS) Serial.println(VOUT * (RANGE_NA ? 0.001 : RANGE_UA ? 1 : 1000)); else
  535. if(LOGGING_FORMAT == LOGGING_FORMAT_MILLIS) Serial.println(VOUT * (RANGE_NA ? 0.000001 : RANGE_UA ? 0.001 : 1)); else
  536. if(LOGGING_FORMAT == LOGGING_FORMAT_ADC) Serial.println(readDiff,0);
  537. }
  538. #ifdef BT_SERIAL_EN
  539. if (BT_LOGGING_ENABLED) {
  540. #ifdef OLED_SUPPORT
  541. if (OLED_found) {
  542. u8g2.setFont(u8g2_font_siji_t_6x10); //https://github.com/olikraus/u8g2/wiki/fntgrpsiji
  543. u8g2.drawGlyph(104, 10, 0xE00B); //BT icon
  544. }
  545. #endif
  546. btInterval = millis();
  547. if (!AUTORANGE) readVOUT();
  548. if (!VOUTCalculated) {
  549. VOUT = readDiff*ldoOptimized*(BIAS?1:OUTPUT_CALIB_FACTOR);
  550. VOUTCalculated=true;
  551. }
  552. if(LOGGING_FORMAT == LOGGING_FORMAT_EXPONENT) { SerialBT.print(VOUT); SerialBT.print("e"); SerialBT.println(RANGE_NA ? -9 : RANGE_UA ? -6 : -3); } else
  553. if(LOGGING_FORMAT == LOGGING_FORMAT_NANOS) SerialBT.println(VOUT * (RANGE_NA ? 1 : RANGE_UA ? 1000 : 1000000)); else
  554. if(LOGGING_FORMAT == LOGGING_FORMAT_MICROS) SerialBT.println(VOUT * (RANGE_NA ? 0.001 : RANGE_UA ? 1 : 1000)); else
  555. if(LOGGING_FORMAT == LOGGING_FORMAT_MILLIS) SerialBT.println(VOUT * (RANGE_NA ? 0.000001 : RANGE_UA ? 0.001 : 1)); else
  556. if(LOGGING_FORMAT == LOGGING_FORMAT_ADC) SerialBT.println(readDiff,0);
  557. }
  558. #endif
  559. //OLED refresh: ~22ms (SCK:1.6mhz, ADC:64samples/DIV16/b111)
  560. #ifdef OLED_SUPPORT
  561. if (OLED_found && millis() - oledInterval > OLED_REFRESH_INTERVAL) //refresh rate (ms)
  562. {
  563. oledInterval = millis();
  564. if (!AUTORANGE) readVOUT();
  565. if (!VOUTCalculated) VOUT = readDiff*ldoOptimized*(BIAS?1:OUTPUT_CALIB_FACTOR);
  566. u8g2.clearBuffer(); //175us
  567. u8g2.setFont(u8g2_font_6x12_tf); //7us
  568. handleVbatRead();
  569. u8g2.setFont(u8g2_font_siji_t_6x10);
  570. if (vbat>4.3)
  571. u8g2.drawGlyph(115, 10, 0xE23A); //charging!
  572. else if(vbat>4.1)
  573. u8g2.drawGlyph(115, 10, 0xE24B); //100%
  574. else if(vbat>3.95)
  575. u8g2.drawGlyph(115, 10, 0xE249); //80%
  576. else if(vbat>3.85)
  577. u8g2.drawGlyph(115, 10, 0xE247); //60%
  578. else if(vbat>3.75)
  579. u8g2.drawGlyph(115, 10, 0xE245); //40%
  580. else if(vbat>3.65)
  581. u8g2.drawGlyph(115, 10, 0xE244); //20%
  582. else if(vbat>LOBAT_THRESHOLD)
  583. u8g2.drawGlyph(115, 10, 0xE243); //5%!
  584. else u8g2.drawGlyph(115, 10, 0xE242); //u8g2.drawStr(88,12,"LoBat!");
  585. u8g2.setFont(u8g2_font_6x12_tf); //7us
  586. if (AUTORANGE) {
  587. u8g2.drawStr(0,12, analog_ref_half ? "AUTO\xb7\xbd" : "AUTO");
  588. u8g2.setCursor(42,12); u8g2.print(readDiff,0);
  589. } else {
  590. if (analog_ref_half) u8g2.drawStr(0,12,"\xbd");
  591. u8g2.setCursor(12,12); u8g2.print(readDiff,0);
  592. }
  593. if (autoffBuzz) u8g2.drawStr(5,26,"* AUTO OFF! *"); //autoffWarning
  594. u8g2.setFont(u8g2_font_helvB24_te);
  595. u8g2.setCursor(RANGE_MA ? 102 : 106, RANGE_UA ? 55:60); u8g2.print(RANGE_UA ? char('µ') : rangeUnit);
  596. u8g2.setFont(u8g2_font_logisoso32_tr);
  597. u8g2.setCursor(0,64); u8g2.print((BIAS&&abs(VOUT)>=0.4||!BIAS&&VOUT>=0.4)?VOUT:0, abs(VOUT)>=1000?0:1);
  598. if (!BIAS && readDiff>ADC_OVERLOAD || BIAS && abs(readDiff)>ADC_OVERLOAD/2)
  599. {
  600. u8g2.setFont(u8g2_font_9x15B_tf);
  601. u8g2.drawStr(0,28, "OVERLOAD!");
  602. }
  603. u8g2.sendBuffer();
  604. }
  605. #endif
  606. #ifdef NEXTION_SUPPORT
  607. if ( millis() - nextionIntervalwrite > NEXTION_REFRESH_INTERVAL) //refresh rate (ms)
  608. {
  609. nextionIntervalwrite =millis() ;
  610. handleVbatRead();
  611. if (!AUTORANGE) readVOUT();
  612. if (!VOUTCalculated) VOUT = readDiff*ldoOptimized*(BIAS?1:OUTPUT_CALIB_FACTOR);
  613. if (nex_page==2) myNex.writeStr("bat.txt",(String)vbat);
  614. if (nex_page==0){
  615. if (vbat>4.3)
  616. myNex.writeNum("p0.pic",6); //charging!
  617. else if(vbat>4.1)
  618. myNex.writeNum("p0.pic",5); ///100%
  619. else if(vbat>3.95)
  620. myNex.writeNum("p0.pic",4); //80%
  621. else if(vbat>3.85)
  622. myNex.writeNum("p0.pic",3); //60%
  623. else if(vbat>3.75)
  624. myNex.writeNum("p0.pic",2); //40%
  625. else if(vbat>3.65)
  626. myNex.writeNum("p0.pic",1); //20%
  627. else
  628. myNex.writeNum("p0.pic",0); //u8g2.drawStr(88,12,"LoBat!");
  629. }
  630. char buff[7];
  631. float currentout=(((BIAS&&abs(VOUT)>=0.4)||(!BIAS&&VOUT>=0.4))?VOUT:0);
  632. snprintf (buff, sizeof(buff), "%f", ((BIAS&&abs(VOUT)>=0.4)||(!BIAS&&VOUT>=0.4))?VOUT:0, (abs(VOUT)>=1000?0:1));
  633. if ((!BIAS && readDiff>ADC_OVERLOAD) || (BIAS && abs(readDiff)>ADC_OVERLOAD/2))
  634. {
  635. if (nex_page == 0 || nex_page==1 || nex_page==6)
  636. myNex.writeStr("current.txt","OVERLOAD!");
  637. }else{
  638. if (nex_page == 0 || nex_page==1 || nex_page==6)
  639. myNex.writeStr("current.txt",String(buff)+rangeUnit+'A');
  640. if (nex_page==1){
  641. int Value = (uint8_t) map(currentout,0,3300,0,255); //Read the pot value ann map it to 0.255 (max value of waveform=255)
  642. String Tosend = "add "; //We send the string "add "
  643. Tosend += 1; //send the id of the block you want to add the value to
  644. Tosend += ",";
  645. Tosend += 0; //Channel of taht id, in this case channel 0 of the waveform
  646. Tosend += ",";
  647. Tosend += Value ; //Send the value and 3 full bytes
  648. myNex.writeStr(Tosend,"cmd");
  649. }
  650. }
  651. if (nex_page == 5 ){
  652. chronometer();
  653. }
  654. float current_test=currentout;
  655. if (rangeUnit=='m'){
  656. current_test=current_test*1000000;
  657. }
  658. if (rangeUnit=='u'){
  659. current_test=current_test*1000;
  660. }
  661. if (nex_timer_run )
  662. {
  663. Serial.println(currentout);
  664. Serial.println(current_test);
  665. if (nex_timer_set!=0){
  666. if (current_test>nex_timer_set){
  667. nex_timer_run =false;
  668. nex_timer_stop = millis();
  669. nex_timer_trigger=true;
  670. nex_timer_current = String(buff)+rangeUnit+'A';
  671. if (nex_page==5){
  672. myNex.writeNum("stoptimer.bco",NEX_BLUE);
  673. myNex.writeNum("starttimer.bco",NEX_LIGHT_GREY);
  674. myNex.writeStr("timercurrent.txt",nex_timer_current);
  675. }
  676. if (nex_page==0){
  677. myNex.writeNum("timer.pic",21);
  678. }
  679. }
  680. }
  681. }
  682. if (nex_timerstats_run)
  683. {
  684. if (nex_stats_min > current_test)
  685. {
  686. nex_stats_min = current_test;
  687. nex_stats_min_s = String(buff)+rangeUnit+'A';
  688. myNex.writeStr("avgmin.txt",nex_stats_min_s);
  689. }
  690. if (nex_stats_max < current_test)
  691. {
  692. nex_stats_max = current_test;
  693. nex_stats_max_s = String(buff)+rangeUnit+'A';
  694. myNex.writeStr("avgmax.txt",nex_stats_max_s);
  695. }
  696. }
  697. }
  698. #endif
  699. WDTclear();
  700. handleTouchPads(); //~112uS
  701. handleAutoOff();
  702. //Serial.println(micros()-timestamp);
  703. } //loop()
  704. void handleVbatRead() {
  705. //limit how often we read the battery since it's not expected to change a lot
  706. if (millis() - vbatInterval < VBAT_REFRESH_INTERVAL) return;
  707. else vbatInterval = millis();
  708. uint8_t half = analog_ref_half;
  709. if (half) analogReferenceHalf(false);
  710. vbat=adcRead(SENSE_VIN);
  711. if (half) analogReferenceHalf(true);
  712. vbat=((vbat/ADCFULLRANGE) * ldoValue) * 1.5; //1.5 given by vbat->A5 resistor ratio (1 / (2M * 1/(1M+2M)))
  713. /*
  714. syncADC();
  715. ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[SENSE_VIN].ulADCChannelNumber;
  716. ADC->INPUTCTRL.bit.MUXNEG = 0x19;//ioGND
  717. adcRead(); //discard first reading
  718. vbat = adcRead();
  719. syncADC();
  720. ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[SENSE_OUTPUT].ulADCChannelNumber;
  721. ADC->INPUTCTRL.bit.MUXNEG = g_APinDescription[SENSE_GNDISO].ulADCChannelNumber;
  722. syncADC();
  723. */
  724. }
  725. uint16_t valM=0, valU=0, valN=0;
  726. void handleTouchPads() {
  727. if (millis() - touchSampleInterval < TOUCH_SAMPLE_INTERVAL) return;
  728. if (TOUCH_DEBUG_ENABLED) {
  729. Serial.print(qt[2].measure());Serial.print('\t');
  730. Serial.print(qt[1].measure());Serial.print('\t');
  731. Serial.println(qt[0].measure());
  732. }
  733. bool MA_PRESSED = qt[2].measure()>TOUCH_HIGH_THRESHOLD;
  734. bool UA_PRESSED = qt[1].measure()>TOUCH_HIGH_THRESHOLD;
  735. bool NA_PRESSED = qt[0].measure()>TOUCH_HIGH_THRESHOLD;
  736. touchSampleInterval = millis();
  737. if (MA_PRESSED || UA_PRESSED || NA_PRESSED) lastKeepAlive=millis();
  738. //range switching
  739. if (!AUTORANGE) {
  740. if (MA_PRESSED && !UA_PRESSED && !NA_PRESSED && rangeUnit!='m') { rangeMA(); rangeBeep(20); }
  741. if (UA_PRESSED && !MA_PRESSED && !NA_PRESSED && rangeUnit!='u') { rangeUA(); rangeBeep(20); }
  742. if (NA_PRESSED && !UA_PRESSED && !MA_PRESSED && rangeUnit!='n') { rangeNA(); rangeBeep(20); }
  743. }
  744. //LPF activation --- [NA+UA]
  745. if (UA_PRESSED && NA_PRESSED && !MA_PRESSED && millis()-lpfInterval>1000) { toggleLPF(); Beep(3, false); }
  746. //offset toggling (GNDISO to half supply) --- [MA+UA]
  747. if (MA_PRESSED && UA_PRESSED && !NA_PRESSED && millis()-offsetInterval>1000) { toggleOffset(); Beep(3, false); }
  748. //AUTORANGE toggling
  749. if (MA_PRESSED && NA_PRESSED && !UA_PRESSED && millis()-autorangeInterval>1000) { toggleAutoranging(); Beep(20, false); delay(50); Beep(20, false); }
  750. }
  751. void rangeMA() {
  752. rangeUnit='m';
  753. PIN_ON(MA_PIN);
  754. PIN_OFF(UA_PIN);
  755. PIN_OFF(NA_PIN);
  756. if (GPIO_HEADER_RANGING) {
  757. PIN_ON(MA_GPIO_PIN);
  758. PIN_OFF(UA_GPIO_PIN);
  759. PIN_OFF(NA_GPIO_PIN);
  760. }
  761. analogReferenceHalf(true);
  762. #ifdef BT_OUTPUT_ADC
  763. if (BT_found) SerialBT.println("RANGE: MA");
  764. #endif
  765. #ifdef NEXTION_SUPPORT
  766. myNex.writeNum("b0.bco",NEX_LIGHT_GREY);
  767. myNex.writeNum("b1.bco",NEX_LIGHT_GREY);
  768. myNex.writeNum("b2.bco",NEX_BLUE);
  769. #endif
  770. }
  771. void rangeUA() {
  772. rangeUnit='u';
  773. PIN_OFF(MA_PIN);
  774. PIN_ON(UA_PIN);
  775. PIN_OFF(NA_PIN);
  776. if (GPIO_HEADER_RANGING) {
  777. PIN_OFF(MA_GPIO_PIN);
  778. PIN_ON(UA_GPIO_PIN);
  779. PIN_OFF(NA_GPIO_PIN);
  780. }
  781. analogReferenceHalf(true);
  782. #ifdef BT_OUTPUT_ADC
  783. if (BT_found) SerialBT.println("RANGE: UA");
  784. #endif
  785. #ifdef NEXTION_SUPPORT
  786. myNex.writeNum("b0.bco",NEX_LIGHT_GREY);
  787. myNex.writeNum("b1.bco",NEX_BLUE);
  788. myNex.writeNum("b2.bco",NEX_LIGHT_GREY);
  789. #endif
  790. }
  791. void rangeNA() {
  792. rangeUnit='n';
  793. PIN_OFF(MA_PIN);
  794. PIN_OFF(UA_PIN);
  795. PIN_ON(NA_PIN);
  796. if (GPIO_HEADER_RANGING) {
  797. PIN_OFF(MA_GPIO_PIN);
  798. PIN_OFF(UA_GPIO_PIN);
  799. PIN_ON(NA_GPIO_PIN);
  800. }
  801. analogReferenceHalf(true);
  802. #ifdef BT_OUTPUT_ADC
  803. if (BT_found) SerialBT.println("RANGE: NA");
  804. #endif
  805. #ifdef NEXTION_SUPPORT
  806. myNex.writeNum("b0.bco",NEX_BLUE);
  807. myNex.writeNum("b1.bco",NEX_LIGHT_GREY);
  808. myNex.writeNum("b2.bco",NEX_LIGHT_GREY);
  809. #endif
  810. }
  811. void handleAutoOff() {
  812. uint32_t autooff_deadline = uint32_t((autooff_interval == AUTOOFF_SMART && !(USB_LOGGING_ENABLED || BT_LOGGING_ENABLED))?AUTOOFF_DEFAULT:autooff_interval)*1000;
  813. if (millis() - lastKeepAlive > autooff_deadline - 5*1000) {
  814. autoffWarning = true;
  815. if (millis()-autoOffBuzzInterval> AUTOOFF_BUZZ_DELAY)
  816. {
  817. autoOffBuzzInterval = millis();
  818. autoffBuzz=!autoffBuzz;
  819. if (autoffBuzz)
  820. tone(BUZZER, NOTE_B5);
  821. else
  822. noTone(BUZZER);
  823. }
  824. if (millis() - lastKeepAlive > autooff_deadline) {
  825. pinMode(AUTOFF, OUTPUT);
  826. digitalWrite(AUTOFF, LOW);
  827. }
  828. }
  829. else if (autoffWarning) { autoffWarning=autoffBuzz=false; digitalWrite(AUTOFF, HIGH); noTone(BUZZER); }
  830. }
  831. void toggleLPF() {
  832. LPF=!LPF;
  833. lpfInterval = millis();
  834. digitalWrite(LPFPIN, LPF);
  835. digitalWrite(LPFLED, LPF);
  836. if (AUTORANGE && !LPF) toggleAutoranging(); //turn off AUTORANGE
  837. #ifdef NEXTION_SUPPORT
  838. if (LPF) myNex.writeNum("lpf.pic",11);
  839. else myNex.writeNum("lpf.pic",15);
  840. #endif
  841. }
  842. void toggleOffset() {
  843. BIAS=!BIAS;
  844. offsetInterval = millis();
  845. analogWrite(A0, (BIAS ? DAC_HALF_SUPPLY_OFFSET : DAC_GND_ISO_OFFSET));
  846. digitalWrite(BIAS_LED, BIAS);
  847. if (AUTORANGE && BIAS) toggleAutoranging(); //turn off AUTORANGE
  848. #ifdef NEXTION_SUPPORT
  849. if (BIAS) myNex.writeNum("bias.pic",12);
  850. else myNex.writeNum("bias.pic",14);
  851. #endif
  852. }
  853. void toggleAutoranging() {
  854. autorangeInterval = millis();
  855. AUTORANGE=!AUTORANGE;
  856. if (AUTORANGE && BIAS) toggleOffset(); //turn off BIAS
  857. if (AUTORANGE && !LPF) toggleLPF(); //turn on BIAS
  858. #ifdef NEXTION_SUPPORT
  859. if (AUTORANGE) myNex.writeNum("b3.bco",NEX_BLUE);
  860. else myNex.writeNum("b3.bco",NEX_LIGHT_GREY);
  861. #endif
  862. }
  863. void Beep(byte theDelay, boolean twoSounds) {
  864. tone(BUZZER, TONE_BEEP, theDelay);
  865. if (twoSounds)
  866. {
  867. delay(10);
  868. tone(BUZZER, 4500, theDelay);
  869. }
  870. }
  871. static __inline__ void syncADC() __attribute__((always_inline, unused));
  872. static void syncADC() {
  873. while(ADC->STATUS.bit.SYNCBUSY == 1);
  874. }
  875. void setupADC() {
  876. ADC->CTRLA.bit.ENABLE = 0; // disable ADC
  877. syncADC();
  878. ADC->REFCTRL.bit.REFCOMP = 1;
  879. ADC->CTRLB.reg = ADC_PRESCALER | ADC_CTRLB_RESSEL_12BIT;
  880. ADC->AVGCTRL.reg = ADC_AVGCTRL;
  881. ADC->SAMPCTRL.reg = ADC_SAMPCTRL;
  882. ADC->CTRLA.bit.ENABLE = 1; // enable ADC
  883. syncADC();
  884. // // ADC Linearity/Bias Calibration from NVM (should already be done done in core)
  885. // uint32_t bias = (*((uint32_t *) ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos;
  886. // uint32_t linearity = (*((uint32_t *) ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos;
  887. // linearity |= ((*((uint32_t *) ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5;
  888. // ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(linearity);
  889. }
  890. int adcRead(byte ADCpin) {
  891. ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[ADCpin].ulADCChannelNumber;
  892. syncADC();
  893. ADC->SWTRIG.bit.START = 1;
  894. while (ADC->INTFLAG.bit.RESRDY == 0);
  895. ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY;
  896. syncADC();
  897. return ADC->RESULT.reg;
  898. }
  899. void readVOUT() {
  900. readDiff = adcRead(SENSE_OUTPUT) - adcRead(SENSE_GNDISO) + offsetCorrectionValue;
  901. if (!analog_ref_half && readDiff > RANGE_SWITCH_THRESHOLD_LOW && readDiff < RANGE_SWITCH_THRESHOLD_HIGH/3)
  902. {
  903. analogReferenceHalf(true);
  904. readVOUT();
  905. }
  906. else if (analog_ref_half && readDiff >= RANGE_SWITCH_THRESHOLD_HIGH)
  907. {
  908. analogReferenceHalf(false);
  909. readVOUT();
  910. }
  911. }
  912. void analogReadCorrectionForced(int offset, uint16_t gain) {
  913. offsetCorrectionValue=offset;
  914. gainCorrectionValue=gain;
  915. analogReadCorrection(offset,gain);
  916. }
  917. void WDTset() {
  918. // Generic clock generator 2, divisor = 32 (2^(DIV+1))
  919. GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4);
  920. // Enable clock generator 2 using low-power 32KHz oscillator. With /32 divisor above, this yields 1024Hz(ish) clock.
  921. GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_DIVSEL;
  922. while(GCLK->STATUS.bit.SYNCBUSY);
  923. // WDT clock = clock gen 2
  924. GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2;
  925. WDT->CTRL.reg = 0; //disable WDT
  926. while(WDT->STATUS.bit.SYNCBUSY);
  927. WDT->INTENCLR.bit.EW = 1; //disable early warning
  928. WDT->CONFIG.bit.PER = 0xA; //period ~8s
  929. WDT->CTRL.bit.WEN = 0; //disable window mode
  930. while(WDT->STATUS.bit.SYNCBUSY);
  931. WDTclear();
  932. WDT->CTRL.bit.ENABLE = 1; //enable WDT
  933. while(WDT->STATUS.bit.SYNCBUSY);
  934. }
  935. uint32_t WDTInterval=0;
  936. void WDTclear() {
  937. if (millis() - WDTInterval > 6999) //pet the dog every 7s
  938. {
  939. WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;
  940. //while(WDT->STATUS.bit.SYNCBUSY);
  941. WDTInterval=millis();
  942. }
  943. }
  944. void ldoOptimizeRefresh() {
  945. if (analog_ref_half)
  946. ldoOptimized = (ldoValue*500)/ADCFULLRANGE;
  947. else
  948. ldoOptimized = (ldoValue*1000)/ADCFULLRANGE;
  949. }
  950. void saveLDO(float newLdoValue) {
  951. ldoValue = newLdoValue;
  952. eeprom_LDO.write(newLdoValue);
  953. ldoOptimizeRefresh();
  954. }
  955. void refreshADCSamplingSpeed() {
  956. if (ADC_SAMPLING_SPEED==ADC_SAMPLING_SPEED_AVG)
  957. ADC_AVGCTRL = ADC_AVGCTRL_SAMPLENUM_64 | ADC_AVGCTRL_ADJRES(0x4ul);
  958. else if (ADC_SAMPLING_SPEED==ADC_SAMPLING_SPEED_FAST)
  959. ADC_AVGCTRL = ADC_AVGCTRL_SAMPLENUM_16 | ADC_AVGCTRL_ADJRES(0x4ul); //take 16 samples adjust by 4
  960. else if (ADC_SAMPLING_SPEED==ADC_SAMPLING_SPEED_SLOW)
  961. ADC_AVGCTRL = ADC_AVGCTRL_SAMPLENUM_256 | ADC_AVGCTRL_ADJRES(0x4ul); //take 512 samples adjust by 4
  962. setupADC();
  963. //other combinations:
  964. //ADC_AVGCTRL_SAMPLENUM_128 | ADC_AVGCTRL_ADJRES(0x4ul)
  965. //ADC_AVGCTRL_SAMPLENUM_1 | ADC_AVGCTRL_ADJRES(0x00ul); // take 1 sample, adjusting result by 0
  966. //ADC_AVGCTRL_SAMPLENUM_16 | ADC_AVGCTRL_ADJRES(0x4ul); //take 16 samples adjust by 4
  967. //ADC_AVGCTRL_SAMPLENUM_256 | ADC_AVGCTRL_ADJRES(0x4ul); //take 256 samples adjust by 4
  968. //ADC_AVGCTRL_SAMPLENUM_512 | ADC_AVGCTRL_ADJRES(0x4ul); //take 512 samples adjust by 4
  969. //ADC_AVGCTRL_SAMPLENUM_1024 | ADC_AVGCTRL_ADJRES(0x4ul); //take 1024 samples adjust by 4
  970. }
  971. void printCalibInfo() {
  972. Serial.println("\r\nADC calibration values:");
  973. Serial.print("Offset="); Serial.println(offsetCorrectionValue);
  974. Serial.print("Gain="); Serial.println(gainCorrectionValue);
  975. Serial.print("LDO="); Serial.println(ldoValue,3);
  976. Serial.println("\r\nEEPROM Settings:");
  977. Serial.print("LoggingFormat="); Serial.println(LOGGING_FORMAT);
  978. Serial.print("ADCSamplingSpeed="); Serial.println(ADC_SAMPLING_SPEED);
  979. Serial.print("AutoOff=");
  980. if (autooff_interval == AUTOOFF_DISABLED) {
  981. Serial.println("DISABLED");
  982. } else if (autooff_interval == AUTOOFF_SMART) {
  983. Serial.println("SMART");
  984. } else {
  985. Serial.println(autooff_interval);
  986. }
  987. Serial.println("");
  988. }
  989. void printSerialMenu() {
  990. // Print device name, firmware version and state for interop on PC side
  991. Serial.println("\r\nCurrentRanger R3");
  992. Serial.print("Firmware version: "); Serial.println(FW_VERSION);
  993. Serial.print("BT Logging: "); Serial.println(BT_LOGGING_ENABLED);
  994. Serial.print("USB Logging: "); Serial.println(USB_LOGGING_ENABLED);
  995. printCalibInfo();
  996. Serial.println("a = cycle Auto-Off function");
  997. Serial.print ("b = toggle BT/serial logging (");Serial.print(SERIAL_UART_BAUD);Serial.println("baud)");
  998. Serial.println("f = cycle serial logging formats (exponent,nA,uA,mA/raw-ADC)");
  999. Serial.println("g = toggle GPIO range indication (SCK=mA,MISO=uA,MOSI=nA)");
  1000. Serial.println("r = reboot into bootloader");
  1001. Serial.println("s = cycle ADC sampling speeds (0=average,faster,slower)");
  1002. Serial.println("t = toggle touchpad serial output debug info");
  1003. Serial.println("u = toggle USB/serial logging");
  1004. Serial.println("< = Calibrate LDO value (-1mV)");
  1005. Serial.println("> = Calibrate LDO value (+1mV)");
  1006. Serial.println("* = Calibrate GAIN value (+1)");
  1007. Serial.println("/ = Calibrate GAIN value (-1)");
  1008. Serial.println("+ = Calibrate OFFSET value (+1)");
  1009. Serial.println("- = Calibrate OFFSET value (-1)");
  1010. Serial.println("? = Print this menu and calib info");
  1011. Serial.println();
  1012. }
  1013. void analogReferenceHalf(uint8_t half) {
  1014. analog_ref_half = half;
  1015. analogReference(half ? AR_INTERNAL1V65 : AR_DEFAULT);
  1016. ldoOptimizeRefresh();
  1017. }
  1018. void analogReadCorrection(int offset, uint16_t gain) {
  1019. ADC->OFFSETCORR.reg = ADC_OFFSETCORR_OFFSETCORR(offset);
  1020. ADC->GAINCORR.reg = ADC_GAINCORR_GAINCORR(gain);
  1021. ADC->CTRLB.bit.CORREN = 1;
  1022. while(ADC->STATUS.bit.SYNCBUSY);
  1023. }
  1024. void rangeBeep(uint16_t switch_delay) {
  1025. uint16_t freq = NOTE_C5;
  1026. if (RANGE_UA) freq = NOTE_D5;
  1027. if (RANGE_MA) freq = NOTE_E5;
  1028. tone(BUZZER, freq, switch_delay?switch_delay:20);
  1029. }
  1030. #define REBOOT_TOKEN 0xf01669ef //special token in RAM, picked up by the bootloader
  1031. void rebootIntoBootloader() {
  1032. *((volatile uint32_t *)(HMCRAMC0_ADDR + HMCRAMC0_SIZE - 4)) = REBOOT_TOKEN; //Entering bootloader from application: https://github.com/microsoft/uf2-samdx1/issues/41
  1033. NVIC_SystemReset();
  1034. }
  1035. void trigger1(){
  1036. lastKeepAlive=millis();
  1037. if (!AUTORANGE){
  1038. rangeNA(); rangeBeep(0);
  1039. }
  1040. }
  1041. void trigger2(){
  1042. lastKeepAlive=millis();
  1043. if (!AUTORANGE){
  1044. rangeUA(); rangeBeep(0);
  1045. }
  1046. }
  1047. void trigger3(){
  1048. lastKeepAlive=millis();
  1049. if (!AUTORANGE){
  1050. rangeMA(); rangeBeep(0);
  1051. }
  1052. }
  1053. void trigger4(){
  1054. lastKeepAlive=millis();
  1055. toggleAutoranging(); Beep(20, false); delay(50); Beep(20, false);
  1056. }
  1057. void trigger16(){
  1058. lastKeepAlive=millis();
  1059. Beep(20, false);
  1060. myNex.writeStr("page page0","cmd");
  1061. if (LPF) myNex.writeNum("lpf.pic",11);
  1062. else
  1063. {
  1064. myNex.writeNum("lpf.pic",15);
  1065. }
  1066. if (BIAS) myNex.writeNum("bias.pic",12);
  1067. else
  1068. {
  1069. myNex.writeNum("bias.pic",14);
  1070. }
  1071. if (AUTORANGE){
  1072. myNex.writeNum("b3.bco",NEX_BLUE);
  1073. }else{
  1074. myNex.writeNum("b3.bco",NEX_LIGHT_GREY);
  1075. }
  1076. if( rangeUnit=='n') myNex.writeNum("b0.bco",NEX_BLUE);
  1077. if( rangeUnit=='u') myNex.writeNum("b1.bco",NEX_BLUE);
  1078. if( rangeUnit=='m') myNex.writeNum("b2.bco",NEX_BLUE);
  1079. if (nex_timer_run){
  1080. myNex.writeNum("timer.pic",20);
  1081. }
  1082. nex_page=0;
  1083. }
  1084. void trigger17(){
  1085. lastKeepAlive=millis();
  1086. Beep(20, false);
  1087. myNex.writeStr("page page5","cmd");
  1088. if( nex_timer_range==0)
  1089. myNex.writeNum("tma.bco",NEX_BLUE) ;
  1090. else
  1091. myNex.writeNum("tma.bco",NEX_LIGHT_GREY) ;
  1092. if( nex_timer_range==1)
  1093. myNex.writeNum("tua.bco",NEX_BLUE) ;
  1094. else
  1095. myNex.writeNum("tua.bco",NEX_LIGHT_GREY) ;
  1096. if( nex_timer_range==2)
  1097. myNex.writeNum("tna.bco",NEX_BLUE) ;
  1098. else
  1099. myNex.writeNum("tna.bco",NEX_LIGHT_GREY) ;
  1100. myNex.writeNum("dec0.val",nex_timer_d0 );
  1101. myNex.writeNum("dec1.val",nex_timer_d1 );
  1102. myNex.writeNum("dec2.val",nex_timer_d2 );
  1103. myNex.writeNum("dec3.val",nex_timer_d3 );
  1104. if (nex_timer_run){
  1105. myNex.writeNum("starttimer.bco",NEX_BLUE);
  1106. myNex.writeNum("stoptimer.bco",NEX_LIGHT_GREY);
  1107. }else{
  1108. myNex.writeNum("stoptimer.bco",NEX_BLUE);
  1109. myNex.writeNum("starttimer.bco",NEX_LIGHT_GREY);
  1110. }
  1111. myNex.writeStr("timercurrent.txt",nex_timer_current);
  1112. nex_page=5;
  1113. }
  1114. void trigger18(){
  1115. Beep(20, false);
  1116. myNex.writeStr("page page6","cmd");
  1117. if (nex_timerstats_run){
  1118. myNex.writeNum("startstats.bco",NEX_BLUE);
  1119. myNex.writeNum("stopstats.bco",NEX_LIGHT_GREY);
  1120. myNex.writeStr("avgmin.txt",nex_stats_min_s);
  1121. myNex.writeStr("avgavg.txt",nex_stats_avg_s);
  1122. myNex.writeStr("avgmax.txt",nex_stats_max_s);
  1123. }else{
  1124. myNex.writeNum("stopstats.bco",NEX_BLUE);
  1125. myNex.writeNum("startstats.bco",NEX_LIGHT_GREY);
  1126. }
  1127. nex_page=6;
  1128. }
  1129. void trigger19(){
  1130. lastKeepAlive=millis();
  1131. Beep(20, false);
  1132. myNex.writeStr("page page1","cmd");
  1133. nex_page=1;
  1134. }
  1135. void trigger20(){
  1136. lastKeepAlive=millis();
  1137. Beep(20, false);
  1138. myNex.writeStr("page page2","cmd");
  1139. if (analog_ref_half)
  1140. {
  1141. analogReferenceHalf(false);
  1142. vbat=adcRead(SENSE_VIN);
  1143. analogReferenceHalf(true);
  1144. }
  1145. else vbat=adcRead(SENSE_VIN);
  1146. vbat=((vbat/ADCFULLRANGE) * ldoValue) * 1.5; //1.5 given by vbat->A5 resistor ratio (1 / (2M * 1/(1M+2M)))
  1147. nex_page=2;
  1148. myNex.writeStr("page page2","cmd");
  1149. myNex.writeStr("offset.txt",(String)offsetCorrectionValue);
  1150. myNex.writeStr("gain.txt",(String)gainCorrectionValue);
  1151. myNex.writeStr("ldo.txt",String(ldoValue,3));
  1152. myNex.writeStr("fw.txt",(String)FW_VERSION);
  1153. myNex.writeStr("bat.txt",(String)vbat);
  1154. }
  1155. void trigger21(){
  1156. lastKeepAlive=millis();
  1157. Beep(20, false);
  1158. myNex.writeStr("page page3","cmd");
  1159. nex_page=3;
  1160. }
  1161. void trigger32(){
  1162. lastKeepAlive=millis();
  1163. Beep(20, false);
  1164. toggleLPF();
  1165. }
  1166. void trigger33(){
  1167. lastKeepAlive=millis();
  1168. Beep(20, false);
  1169. toggleOffset();
  1170. }
  1171. void trigger34(){
  1172. lastKeepAlive=millis();
  1173. Beep(20, false);
  1174. USB_LOGGING_ENABLED=!USB_LOGGING_ENABLED;
  1175. if (USB_LOGGING_ENABLED) myNex.writeNum("usb.pic",10);
  1176. else
  1177. {
  1178. myNex.writeNum("usb.pic",16);
  1179. }
  1180. }
  1181. void trigger35()
  1182. { //bluetooth
  1183. lastKeepAlive=millis();
  1184. Beep(20, false);
  1185. if (BT_found){
  1186. BT_found=false;
  1187. myNex.writeNum("bluetooth.pic",13);
  1188. }
  1189. else
  1190. {
  1191. uint32_t timer=millis();
  1192. while(millis()-timer<1000) //about 1s to respond
  1193. {
  1194. if (SerialBT.available()==2 && SerialBT.read()=='O' && SerialBT.read()=='K')
  1195. {
  1196. BT_found=true;
  1197. break;
  1198. }
  1199. }
  1200. if (BT_found){
  1201. myNex.writeNum("bluetooth.pic",9);
  1202. }else{
  1203. myNex.writeNum("bluetooth.pic",13);
  1204. }
  1205. }
  1206. }
  1207. void update_current_limit()
  1208. {
  1209. nex_timer_set= nex_timer_d0*1000+nex_timer_d1*100+nex_timer_d2*10+nex_timer_d3;
  1210. Beep(20, false);
  1211. }
  1212. void chronometer(void){ //This function print: "New: Actual time"
  1213. unsigned long currentMillis = millis();
  1214. uint32_t diff_time=0;
  1215. if (nex_timer_run){
  1216. diff_time=currentMillis-nex_timer_start;
  1217. }else{
  1218. diff_time=nex_timer_stop-nex_timer_start;
  1219. }
  1220. long days = 0;
  1221. long hours = 0;
  1222. long mins = 0;
  1223. long secs = 0;
  1224. String secs_o = ":";
  1225. String mins_o = ":";
  1226. String hours_o = ":";
  1227. secs = diff_time / 1000; // set the seconds remaining
  1228. mins = secs / 60; //convert seconds to minutes
  1229. hours = mins / 60; //convert minutes to hours
  1230. days = hours / 24; //convert hours to days
  1231. secs = secs - (mins * 60); //subtract the coverted seconds to minutes in order to display 59 secs max
  1232. mins = mins - (hours * 60); //subtract the coverted minutes to hours in order to display 59 minutes max
  1233. hours = hours - (days * 24); //subtract the coverted hours to days in order to display 23 hours max
  1234. if (secs < 10) {
  1235. secs_o = ":0";
  1236. }
  1237. if (mins < 10) {
  1238. mins_o = ":0";
  1239. }
  1240. if (hours < 10) {
  1241. hours_o = ":0";
  1242. }
  1243. myNex.writeStr("timertext.txt",String(hours)+mins_o+String(mins)+secs_o+String(secs));
  1244. }
  1245. void trigger82()
  1246. {
  1247. nex_timer_range=0;
  1248. myNex.writeNum("tma.bco",NEX_BLUE);
  1249. myNex.writeNum("tua.bco",NEX_LIGHT_GREY);
  1250. myNex.writeNum("tna.bco",NEX_LIGHT_GREY);
  1251. Beep(20, false);
  1252. }
  1253. void trigger83()
  1254. {
  1255. nex_timer_range=1;
  1256. myNex.writeNum("tma.bco",NEX_LIGHT_GREY);
  1257. myNex.writeNum("tua.bco",NEX_BLUE);
  1258. myNex.writeNum("tna.bco",NEX_LIGHT_GREY);
  1259. Beep(20, false);
  1260. }
  1261. void trigger84()
  1262. {
  1263. nex_timer_range=2;
  1264. myNex.writeNum("tma.bco",NEX_LIGHT_GREY);
  1265. myNex.writeNum("tua.bco",NEX_LIGHT_GREY);
  1266. myNex.writeNum("tna.bco",NEX_BLUE);
  1267. Beep(20, false);
  1268. }
  1269. void trigger85()
  1270. {
  1271. myNex.writeNum("starttimer.bco",NEX_BLUE);
  1272. myNex.writeNum("stoptimer.bco",NEX_LIGHT_GREY);
  1273. nex_timer_current="";
  1274. myNex.writeStr("timercurrent.txt",nex_timer_current);
  1275. if (!nex_timer_run){
  1276. nex_timer_start=millis();
  1277. nex_timer_run=true;
  1278. }
  1279. Beep(20, false);
  1280. }
  1281. void trigger86()
  1282. {
  1283. myNex.writeNum("stoptimer.bco",NEX_BLUE);
  1284. myNex.writeNum("starttimer.bco",NEX_LIGHT_GREY);
  1285. if (nex_timer_run){
  1286. nex_timer_stop=millis();
  1287. nex_timer_run=false;
  1288. }
  1289. nex_timer_current="";
  1290. myNex.writeStr("timercurrent.txt",nex_timer_current);
  1291. Beep(20, false);
  1292. }
  1293. void trigger87()
  1294. {
  1295. myNex.writeNum("startstats.bco",NEX_BLUE);
  1296. myNex.writeNum("stopstats.bco",NEX_LIGHT_GREY);
  1297. nex_timer_current="";
  1298. if (!nex_timerstats_run){
  1299. nex_timerstats_run=true;
  1300. }
  1301. nex_stats_min = 0;
  1302. nex_stats_count=0;
  1303. nex_stats_max=0;
  1304. nex_stats_avg=0;
  1305. nex_stats_avg_s="0nA";
  1306. nex_stats_min_s="0nA";
  1307. nex_stats_max_s="0nA";
  1308. myNex.writeStr("avgmin.txt",nex_stats_min_s);
  1309. myNex.writeStr("avgavg.txt",nex_stats_avg_s);
  1310. myNex.writeStr("avgmax.txt",nex_stats_max_s);
  1311. Beep(20, false);
  1312. }
  1313. void trigger88()
  1314. {
  1315. myNex.writeNum("stopstats.bco",NEX_BLUE);
  1316. myNex.writeNum("startstats.bco",NEX_LIGHT_GREY);
  1317. if (nex_timerstats_run){
  1318. nex_timerstats_run=false;
  1319. }
  1320. nex_timer_current="";
  1321. nex_stats_min = 0;
  1322. nex_stats_count=0;
  1323. nex_stats_max=0;
  1324. nex_stats_avg=0;
  1325. Beep(20, false);
  1326. }