//11 Sept 2019: adaptation of keyASM3 to add measurement of USB voltage //13 sept; corrected connection of 33k to +Vcc; measured Vcc and calculated factor. // 15 Sept; rewrote case zero as a state machine; changed calculation to enhance precision - Vcc now reading correctly. #include "Keyboard.h" //supports keyboard simulation io to pc #include //supports i2c transfers #include //supports lcd display /* http://www.geeetech.com/wiki/index.php/Serial_I2C_1602_16%C3%972_Character_LCD_Module connections: Vcc - +5V GND - 0V SDA - A4 SCL - A5 the blue box is a potentiometer Spinning the potentimeter clockwise is to increase contrast ,spinning unclockwise is to decrease it */ LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display // pin assignments const byte ledPin = 9; //LED on pin D9 with resistor to GROUND const byte button = 7; //Button switch is connected between pin D7 and ground. Hardware interrupt. const int analogPin = A10; //wiper connects to Analog pin 10; compiler assigns correct number value automatically const int usbVolts = A8; //connected through a 33k:10k divider to Vcc //no need to assign pin numbers for rotary switch as we are reading direct from portF registers byte oldswitch = 16; //ensure at start oldswitch != switchval read from switch - which cannot exceed 8; enum rotaryswitch { ZERO, ONE, TWO, THREE = 4, FOUR, FIVE }; //rotary switch positions THREE=100, FOUR=101, FIVE=110 //using port F to read 6 way switch code #define DDRFmask 0b00001111 //AND mask, sets MSByte as inputs (=0) #define PORTFmask 0b11110000 //OR mask, sets MSByte "outputs" high for input-pullup //messages for display const char * msg_strings[] = {"USB Voltage is","Set speed","Notepad","Characters","Flash Led","Led Off", }; //msg_strings[] is an array of pointers to those messages. bool ledValue = 0; //led Off int flashRate = 100; //rate to flash LED - set later unsigned long tFlash = 0; //recording millis() times for blinkLed and flashLed routines unsigned long timeASCII = 0, timeKSM = 0; //recording millis() times for sendASCII and Notepad routines bool firstLine = 1; //Characters; only print header if its the first line of a sequence //for measurement of usb voltage int vUsb=0; //integer value of reading const int nSamp=64; //number of samples to aggregate const int vRate=8; //time between measurements static unsigned long tVolts = 0; //time of last measurement int vCount = 0; //count up the number of samples long int agg = 0; //aggregate the readings int average, oldavg=0;//calculate average and check if significant change const float factor = 10.455; //convert n to millivolts (approx 2.5 * 1000 / 1024)*(43/10)) int fiddle=300;//fiddle factor due to ? schottky diode drop: as supplied the oriental micro has j1 open. //short out j1 for full vcc then fiddle not needed //For hardware interrupt button debounce: volatile unsigned long lastIntMillis = 0; //this is the time of the last interrupt volatile unsigned long debounceMillis = 500; //dont allow another in less than 0.5 sec //variables shared between ISR and code: must be type volatile. volatile bool toggleVal = 0; // 0=resting, 1=active; do not send chars unless go button pressed void isr() { //this is the hardware interrupt service routine //if there has been enough time since the last interrupt then the switch value has been stable, so this isnt a bounce if (millis() - lastIntMillis > debounceMillis) { toggleVal = 1; lastIntMillis = millis(); //record time of interrupt } } long int readUsb(){ int temp; //total 6msec analogReference(INTERNAL);//set ref to 2.56V delay(1); //delay to allow to settle - keep it short temp=analogRead(usbVolts); delay(1); temp=analogRead(usbVolts); //first two readings after changing reference are not reliable! delay(2); int v = analogRead(usbVolts); analogReference(DEFAULT);//set ref back to Vcc for reading rate pot delay(1); temp=analogRead(usbVolts); delay(1); temp=analogRead(usbVolts); return(v); //as an integer value - supposedly around 465 (1024 * (5 * 10/43 )/2.56) but actually 437 } int readA2() {//read analog input from potentiometer and process it for rate control long analogVal = analogRead(analogPin) + 75; //max value is approx 1020, minimum zero so add 75 analogVal *= analogVal; //square it so high end value has more effect - this can only be a positive number analogVal = analogVal >> 11; //right shift to bring value back to max 600 min 2 //to set a max flash rate of 5msec and slowest about 1 per sec return ((int) analogVal); } byte readSwitch() { //read position of 6 way switch //Direct access to registers for port F follows byte switchval = PINF >> 4; //read data from PORT F pins 18-21 switchval = ~switchval; // invert it switchval &= 0b00000111; //mask off except lowest three bits return (switchval); } void blinkLed() { flashRate = readA2(); //read the speed from the potentiometer if (flashRate <= 25) flashRate = 25; //set a maximum rate to allow PC to respond. flashLed(flashRate); }//blinkLed //dont need case here as only 2 boolean options - simplify to if, else. Can use break with if - else? void flashLed(int s) { flashRate = s; switch (ledValue) { case 0: { if (millis() - tFlash > flashRate) { //LED is OFF: if its time to change the led state .. tFlash = millis(); ledValue = 1; digitalWrite(ledPin, ledValue); //turn it ON } break; } case 1: { if (millis() - tFlash > flashRate) { //LED is ON: if its time to change the led state .. tFlash = millis(); ledValue = 0; digitalWrite(ledPin, ledValue); //turn it OFF } break; } } }//flashLed void showmessage(char * showit[], int m) { if (m != oldswitch) { //only update display if setting has changed lcd.clear(); lcd.backlight(); //turn backlight on - in case its off lcd.setCursor ( 0, 0 ); // set to col:row - eg 1,8 is 8 chars in on of lower line lcd.print(showit[m]); lcd.setCursor ( 0, 1 ); // set to centre of lower line lcd.print("Position "); lcd.print(m); lcd.print(";"); oldswitch = m; //only change display if value has changed } } bool sendASCII( void ){ bool allSent = 0; static byte asciiVal = 0x21; //static so the value is preserved between calls to this routine flashRate = readA2(); //read the speed from the potentiometer if (firstLine) { Keyboard.println("skillbank.co.uk testing keyboard entry"); Keyboard.println (""); // cr lf between numbers, caps, miniscules firstLine = 0; } if ( (millis() - timeASCII) < flashRate) return(0); // if its not time to send character //when it is time, send one character and space timeASCII = millis(); //record time now Keyboard.write(asciiVal); Keyboard.print(" "); asciiVal++; //incremement printable character variable until '~' if ( (asciiVal == 0x40) || (asciiVal == 0x60) || (asciiVal == 0x7f) ) { Keyboard.println (""); // cr lf between numbers, caps, miniscules } if ( asciiVal > 0x7e ) { //end of printable characters Keyboard.println (""); asciiVal = 0x21; allSent = 1; //all characters sent } //if last char sent else allSent = 0; //not sent last char return (allSent); } //SendASCII bool notepad() { enum stateKSM {INIT, LAUNCH, DELAY, NOTEPAD, PRINTMSG, END}; static byte stateKSM = INIT; static bool KSMStatus = 0; static unsigned long timeNow; timeNow = millis(); //record time at start of routine // note - can not be too fast or does not operate correctly; timings are a bit critical switch ( stateKSM ) { case INIT: KSMStatus = 0; timeKSM = timeNow; stateKSM = LAUNCH; break; case LAUNCH: if ( timeNow - timeKSM >= 20 ) { Keyboard.press(KEY_LEFT_GUI); Keyboard.press('r'); timeKSM = timeNow; stateKSM = DELAY; } break; case DELAY: if ( timeNow - timeKSM >= 20 ) { Keyboard.releaseAll(); //need to release after Keyboard.press timeKSM = timeNow; stateKSM = NOTEPAD; } break; case NOTEPAD: if ( timeNow - timeKSM >= 100) //allow time to launch search box { Keyboard.println("notepad.exe"); //includes return timeKSM = timeNow; stateKSM = PRINTMSG; } break; case PRINTMSG: if ( timeNow - timeKSM >= 500 ) //allow 500ms for notepad to launch { Keyboard.println("Test launch and send characters to Notepad"); timeKSM = timeNow; stateKSM = END; } break; case END: if ( timeNow - timeKSM >= 20 ) { timeKSM = timeNow; stateKSM = INIT; // prepare to run again if button pressed KSMStatus = 1; //flag all done } break; } //switch ( stateKSM ) return (KSMStatus); } //notepad void setup() { Serial.begin(9600); //in case testing needed Keyboard.begin(); lcd.init(); // initialize the lcd //set up button press to generate hardware interrupt pinMode (button, INPUT_PULLUP); //this is our interrupt button //Set Port F up to read rotary switch code //Direct access to registers for port F follows DDRF &= DDRFmask; //to set pins 18-21 as inputs PORTF |= PORTFmask; //with input-pullup pinMode(ledPin, OUTPUT); //to show activity attachInterrupt(digitalPinToInterrupt(button), isr, FALLING); } void loop() { switch (readSwitch()) { case ZERO: { //read and display usb voltage //take a reading if(millis()-tVolts>=vRate){ vUsb = readUsb(); //measure voltage vCount++; agg = agg + vUsb; tVolts = millis(); //record time at end of each reading } //if we have collected nsamp readings if (vCount>=nSamp){ flashLed(2);//turn led on or off //print title and usb voltage lcd.clear(); lcd.backlight(); //turn backlight on - in case its off lcd.setCursor ( 0, 0 ); // set to col:row - eg 1,8 is 8 chars in on of lower line lcd.print(msg_strings[0]); lcd.setCursor ( 0, 1 ); // set to left of lower line lcd.print("mV: "); //Serial.print(agg); //average = agg / nSamp; //NB not used as this reduces precision //Serial.print(": vUsb is: "); vUsb = (agg * factor)/nSamp; //multiply values first, then divide to integer around 5000. //Serial.println(vUsb); //vUsb += fiddle; //fiddle factor for my Micro board where Vcc = 0.3V less than vUsb lcd.print(vUsb); lcd.print(";"); agg=0; vCount=0; //ready for next set of readings } oldswitch=0; //to ensure showmessage works on next switch pos. break; } case ONE: { showmessage(msg_strings, 1); blinkLed(); break; } case TWO: { //launch notepad showmessage(msg_strings, 2); blinkLed(); //wait for "go" if (toggleVal) { byte done_npd = notepad(); //launch payload if (done_npd) toggleVal = 0; } break; } case THREE: { //print character set showmessage(msg_strings, 3); blinkLed(); //wait for "go" if (toggleVal) { byte done = sendASCII(); if (done) toggleVal = 0; // if all chars sent dont repeat until go pressed again } break; } case FOUR: { showmessage(msg_strings, 4); flashLed(100); break; } case FIVE: { showmessage(msg_strings, 5); digitalWrite(ledPin, 0); //LED OFF break; } default: { //default is optional - only happens if theres a fault on the switch } }//switch (readSwitch()) }