#include "main.h"
SD_Card sdc; //SD-Card
RTC_PCF8523 rtc; //real time clock (timer.h timer.cpp)
TheMatrix display; //The matrix orbital display
Light_Source dl;
UDT_TRAMP udt; //ADS1015_ADDRESS (0x48)
UDT_TRAMP udt2{ADS1015_ADDRESS2}; //Member initializer
USBHost usb;
KeyboardController keyboard(usb);
EthernetClient EtherClient;
EthernetClient PASVClient;
IPAddress ServerByAddress(0, 0, 0, 0);
void setup() {
delay(3000);
pinMode(BUTTON1, INPUT_PULLUP); //uppper left
pinMode(BUTTON2, INPUT_PULLUP); //in the middle left
pinMode(BUTTON3, INPUT_PULLUP); //lower left
pinMode(BUTTON4, INPUT_PULLUP); //upper right
pinMode(BUTTON5, INPUT_PULLUP); //in the middle right
pinMode(BUTTON6, INPUT_PULLUP); //lower right
Serial.begin(115200); //Serial Monitor
SerialUSB.begin(115200); //done 20210120 scanner doesnt work power?
Serial.println("hello1");
pinMode(4, OUTPUT); //At Pin 4 is the SD-Card
digitalWrite(4, HIGH); //At Pin 4 is the SD-Card
SD.begin(4); //At Pin 4 is the SD-Card
Serial.println("hello2");
rtc.begin(); //Real time clock
Serial.println("hello3");
udt.begin(0); //TRAMP 1
Serial.println("hello4");
udt2.begin(1); //TRAMP 2 not needed
Serial.println("hello5");
Wire1.begin(); //All I2C communication
Serial.println(F("hello6"));
dl.init(); //Daylight source
Serial.println(F("hello7"));
dl.setDl_off();
sdc.loadCalibratFile();
Serial.println(F("hello8"));
DateTime now = rtc.now();
display.runScriptFile(F("TPSPORTS\\Screen2\\Screen2.bin"));
g_warmUpTimer.resetTimer();
g_state=WARM_UP_TIMER;
display.updateLabel(10,F("Bitte warten"));
display.updateLabel(11, F("Konfiguriere Ethernet, 1Min."));
if (Ethernet.begin(mac) == 0) { //Tries to make connection to network(initialisation), this takes a while and even longer if no cable to the ethernet is conneted.
display.updateLabel(11, F("Kein Ethernet"));
g_flag_ethernet=false;
}
}
/*
* Reads the buttons and debouncing
*/
void checkButtonActivity()
{
g_buttonPressed = display.getID(); //For buttons higher than 6, which are on the touchpanel
if (!digitalRead(BUTTON1))//if pulled down, active
{
while (!digitalRead(BUTTON1)) // wait as long as pulled down
delay(100);
g_buttonPressed = 1; //set global variable to button for analysis
} // endif BUTTON1
if (!digitalRead(BUTTON2))
{
while (!digitalRead(BUTTON2))
delay(100);
g_buttonPressed = 2;
} // endif BUTTON2
if (!digitalRead(BUTTON3))
{
while (!digitalRead(BUTTON3))
delay(100);
g_buttonPressed = 3;
} // endif BUTTON3
if (!digitalRead(BUTTON4))
{
while (!digitalRead(BUTTON4))
delay(100);
g_buttonPressed = 4;
} // endif BUTTON4
if (!digitalRead(BUTTON5))
{
while (!digitalRead(BUTTON5))
delay(100);
g_buttonPressed = 5;
} // endif BUTTON5
if (!digitalRead(BUTTON6))
{
while (!digitalRead(BUTTON6))
delay(100);
//the fallowing code is needed that the worker do not press fast several times and the screen skips to fast forward automaticly.
g_currentMillis_locked_repress=millis();//Store time since begin of system in variable
if(g_currentMillis_locked_repress - g_startMillis_locked_repress >= g_period_locked_repress) {
//Should the difference be bigger than a set constant. So is the press of the button valid all other are invalid because to fast repressed.
g_startMillis_locked_repress=g_currentMillis_locked_repress; //Store last time of valid pressing
g_buttonPressed = 6;
}
} // endif BUTTON6
} // end checkButtonActivity()
/*
* Ethernet Recieve, looks for answer from FTP server
*/
byte eRcv()
{
byte respCode;
byte thisByte;
char outCount; //was global in example
while (!EtherClient.available()) delay(1);
respCode = EtherClient.peek();
outCount = 0;
while (EtherClient.available())
{
thisByte = EtherClient.read();
Serial.write(thisByte);
if (outCount < 127)
{
g_outBuf[outCount] = thisByte;
outCount++;
g_outBuf[outCount] = 0;
}
}
if (respCode >= '4')
{
//efail();
return 0;
}
return 1;
}
/*
* Upload data files to FTP server
*/
byte uploadDataToFTP(){
static bool once=false;
uint16_t sum;
String str;
File root;
File myFile;
File dir;
String temp;
DNSClient dns;
if(once==false){ // Only count once, all files in folder ftp
display.updateLabel(10,F("Zaehle Dateien fuer Upload"));
delay(1000);
sum= sdc.count("/ftp");
display.updateLabel(10,F("Anz. Dateien fuer Upload bereit"));
display.updateLabel(11,sum); //clear text box
delay(1000);
once =true;
}
if(g_flag_ethernet){//is set on start up when connection with ethernet is possible
dir = SD.open("/ftp");
display.updateLabel(10,F("uploading"));
while(dir.openNextFile()==true){
dns.begin(Ethernet.dnsServerIP()); //Domain Name System
dns.getHostByName(dns_name_of_server,ServerByAddress); //solves hoste name to a IP-address
EtherClient.connect(ServerByAddress,21); //connects to ftp-server
if(EtherClient.connected()){
if (!eRcv()) return 0;
EtherClient.println(ftp_user);//writes login user
if (!eRcv()) return 0;
EtherClient.println(ftp_pw);//writes login password
if (!eRcv()) return 0;
EtherClient.println(F("SYST"));//sending FTP-COMMAND
if (!eRcv()) return 0;
EtherClient.println(F("Type I"));//sending FTP-COMMAND
if (!eRcv()) return 0;
EtherClient.println(F("PASV"));//sending FTP-COMMAND, changing to Passive-Upload
if (!eRcv())return 0 ;//
char *tStr = strtok(g_outBuf, "(,");//handling passive mode answer with the ports
int array_pasv[6];
for ( int i = 0; i < 6; i++) { //reading up to 6 answers
tStr = strtok(NULL, "(,");
array_pasv[i] = atoi(tStr);
if (tStr == NULL)
{;
}
}
unsigned int hiPort, loPort;
hiPort = array_pasv[4] << 8; //shifting answer, editing
loPort = array_pasv[5] & 255; //masking answer, editing
hiPort = hiPort | loPort; //making 2 to 1
PASVClient.connect(ServerByAddress, hiPort);//connecting another client to passive port
dir = SD.open("/ftp");// opens folder FTP
File entry = dir.openNextFile();//opens a file
temp = "/ftp/" + (String)entry.name(); //completes folder with file
myFile=SD.open(temp);//really opens the file in certain location
while(myFile.available()){//as long as there is chars from file print it to passive port ftp-server
PASVClient.print(char(myFile.read()));
}
myFile.close();// close file
SD.remove(temp); // delete file
display.updateLabel(11,--sum);
EtherClient.print(F("STOR "));//command ftp-server storing
EtherClient.println(entry.name()); //give the name of the file to ftp-server
PASVClient.stop();//stopping passive connection
EtherClient.println(F("QUIT"));//command ftp-server to leave
EtherClient.stop(); //stopping ethernet connection
}else{
;//not connected
return 0;
}
}
display.updateLabel(10,F(" "));
display.updateLabel(11,F(" "));
g_flag_ethernet=false; //stop uploading from reentering the code above.
}
}
/*
* State warm up timer 10mins after start to get the bulbs stable
*/
void warmUpTimer()
{
if(g_warmUpTimer.getRemainingMinutes() >= 0){// as long as timer not zero look only for button 5 for special menu
switch (g_buttonPressed)
{
case 5: //If pressed go in special mode / code pad
display.runScriptFile(F("TPSPORTS\\Screen3\\Screen3.bin")); //load screen
g_state = CODE_PAD ; //FSM (finite state machine)
break;
default:
break;
}
display.updateLabel(14, (g_warmUpTimer.getRemainingMinutes()+1));// show minutes remaining
uploadDataToFTP();
}else{ //Timer has run out so scan for 2 buttons
switch (g_buttonPressed)
{
case 5: //Button code pad
display.runScriptFile(F("TPSPORTS\\Screen3\\Screen3.bin")); //load screen
g_state = CODE_PAD ; //FSM
break;
case 6: //Button Forward
display.runScriptFile(F("TPSPORTS\\Screen7\\Screen7.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_OPTRAY; //FSM
break;
default:
break;
} // endswitch
}
} //
/*
* clears code in code_pad mode
*/
void clearCode()
{
for(int i =0;i<4;i++){
g_input_code[i] = 0;
}
g_printable_chars=0;
g_input_index=0;
} //end clearCod
/*
* reads and controls the characters of code_pad
*/
void updateIndices()
{
++g_input_index; //counter for position of input code
if (g_input_index >= 4)
g_input_index = 0;
++g_printable_chars; //counter for length of input code
if (g_printable_chars > 4)
g_printable_chars = 4;
} // end updateIndices()
/*
* Actual code_pad, read buttons and evaluation of code
*/
void codePad(){
bool lock = true;
display.updateLabel(14, F(" ")); //clear text box
while(lock){ //lock processor time in here for proper processing
char check;
check = display.getID(); //read in code of buttons
switch (check) //compare input buttons
{
case 10: //delete text box
clearCode();
display.updateLabel(14, F(" "));
break;
case 11: //Ok button pressed, now compare input code with reference
if(strncmp(g_input_code,"4567",4)==0){ //if match then go in special menu
display.runScriptFile(F("TPSPORTS\\Screen4\\Screen4.bin")); //load screen
g_state = SERVICE_MAIN ; //FSM
lock=false; // release process in order to switch through FSM again.
}
break;
case 12: //goes back to warm up timer
display.runScriptFile(F("TPSPORTS\\Screen2\\Screen2.bin")); //load screen
g_state = WARM_UP_TIMER ; //FSM
lock=false;
break;
case 255: //special code comand from display which has to be thrown away
break;
default: //each button has a code this makes a char out of the input code
g_input_code[g_input_index] = check + 48; // shift in the ASCII-table by 48
updateIndices();
display.updateLabel(14,g_input_code , g_printable_chars);
break;
} // endswitch
}
}
/*
* In service mode
* This function handles the rtc (Real time clock) settings
*/
void serviceRTC(){
bool once = false;
uint8_t Hour = 7;
uint8_t Min = 0;
uint8_t Day = 1;
uint8_t Month = 1;
uint16_t Year = 18;
bool save = false;
DateTime now = rtc.now();
Year = now.year();
Year -= 2000;
Month = now.month();
Day = now.day();
Hour = now.hour();
Min = now.minute();
if (!once) { //do it only once
once = true;
display.updateLabel(3, Day);
display.updateLabel(2, Month);
display.updateLabel(1, Year);
display.updateLabel(4, Hour);
display.updateLabel(5, Min);
}
while (!save) //do while a certain Button is pressed in this case number 3
{
checkButtonActivity(); //read Buttons
switch (g_buttonPressed)
{
case 3: //Button Test
display.runScriptFile(F("TPSPORTS\\Screen4\\Screen4.bin")); //load screen
g_state = SERVICE_MAIN ; //FSM
save = true; //set to true to leave do while
break;
case 6: //Button Test
rtc.adjust(DateTime(Year + 2000, Month, Day, Hour, Min, 0));
save = true; delay(100);
display.runScriptFile(F("TPSPORTS\\Screen5\\Screen5.bin")); //load screen
g_state = SERVICE_RTC ; //FSM
break;
case 13: //Button 11 on touch screen
Day++;
if (Day > 31)
Day = 0;
//delay(100);
display.updateLabel(3, Day);
break;
case 12: //Button 12 on touch screen
Month++;
if (Month > 12)
Month = 1;
//delay(100);
display.updateLabel(2, Month);
break;
case 11: //Button 11 on touch screen
Year++;
if (Year > 40)
Year = 20;
//delay(100);
display.updateLabel(1, Year);
break;
case 14: //Button 14 on touch screen
Hour++;
if (Hour > 23)
Hour = 0;
//delay(100);
display.updateLabel(4, Hour);
break;
case 15: //Button 15 on touch screen
Min++;
if (Min > 59)
Min = 0;
//delay(100);
display.updateLabel(5, Min);
break;
default:
break;
} // endswitch
} // endwhile
}
/*
* helping function of clibrationDL() which refreshes the screen
*/
void updateLabelAndSetLQ(uint16_t& var, bool & toggle, bool & toggle2, bool & toggle3) {
TheMatrix::updateLabel(10, var);
if (toggle) { //twilight
sdc.set_lux_to_twilight = var; //newest changed value, store in variable
dl.dl_twilight = sdc.set_lux_to_twilight; //transfer of into day light class
dl.setDl_value(var); //turn on
}
if (toggle2) { //winter
sdc.set_lux_to_winter = var;
dl.dl_winter = sdc.set_lux_to_winter; //transfer of into day light class
dl.setDl_value(var); //turn on
}
if (toggle3) { //summer
sdc.set_lux_to_summer = var;
dl.dl_summer = sdc.set_lux_to_summer; //transfer of into day light class
dl.setDl_value(var); //turn on
}
}
/*
* manages the daylight calibration
*/
void serviceCalibration(){
static bool toggle_twilight=false;
static bool toggle_winter=false;
static bool toggle_summer=false;
static uint16_t var;
switch (g_buttonPressed)
{
case 3: //Button 3
display.runScriptFile(F("TPSPORTS\\Screen4\\Screen4.bin")); //load screen
g_state = SERVICE_MAIN ; //FSM
toggle_twilight = false;
toggle_winter = false;
toggle_summer = false;
dl.setDl_off();
break;
case 6: //Button 6, save calibration value
if (toggle_twilight) { //Check which mode twilight/winter/summer is active
sdc.set_lux_to_twilight = var;
}
if (toggle_winter) {
sdc.set_lux_to_winter = var;
}
if (toggle_summer) {
sdc.set_lux_to_summer = var;
}
toggle_twilight = false;
toggle_winter = false;
toggle_summer = false;
sdc.writeCalibratFile();
delay(250);
display.updateLabel(10, F("OFF"));
dl.setDl_off();
break;
default:
break;
}
if (g_buttonPressed == 10) { //this is button 10 on touch screen (twilight) day light
toggle_twilight = toggle_twilight ? false : true; // active?
toggle_winter = false;
toggle_summer = false;
if (toggle_twilight) { //if active, twilight, load current value and turn light source on
sdc.loadCalibratFile();
var = sdc.set_lux_to_twilight;
display.updateLabel(10, var);
dl.setDl_value(var);
} else {
display.TheMatrix::updateLabel(10, F("OFF"));
dl.setDl_off();
}
}
if (g_buttonPressed == 11) { //this is button 11 on touch screen (winter) day light
toggle_winter = toggle_winter ? false : true; // active?
toggle_twilight = false;
toggle_summer = false;
if (toggle_winter) { //if active, winter, load current value and turn light source on
sdc.loadCalibratFile();
var = sdc.set_lux_to_winter;
display.updateLabel(10, var);
dl.setDl_value(var);
} else {
display.TheMatrix::updateLabel(10, F("OFF"));
dl.setDl_off();
}
}
if (g_buttonPressed == 12) { //this is button 12 on touch screen (summer) day light
toggle_summer = toggle_summer ? false : true; // active?
toggle_twilight = false;
toggle_winter = false;
if (toggle_summer) { //if active, summer, load current value and turn light source on
sdc.loadCalibratFile();
var = sdc.set_lux_to_summer;
display.updateLabel(10, var);
dl.setDl_value(var);
} else {
display.TheMatrix::updateLabel(10, F("OFF"));
dl.setDl_off();
}
}
if ((g_buttonPressed == 21) && ((toggle_twilight) || (toggle_winter) || (toggle_summer))) { //touch button 21 on screen UP
var++;
if (var == 4096) { //max possible value on source
var = 4095;
}
updateLabelAndSetLQ(var, toggle_twilight, toggle_winter, toggle_summer);
}
if ((g_buttonPressed == 22) && ((toggle_twilight) || (toggle_winter) || (toggle_summer))) { //touch button 22 on screen DOWN
var--;
if (var == 65535) { //overflow
var = 0;
}
updateLabelAndSetLQ(var, toggle_twilight, toggle_winter, toggle_summer);
}
}
/*
* main menue service mode
*/
void serviceMain(){
switch (g_buttonPressed)
{
case 1: //Button 1
display.runScriptFile(F("TPSPORTS\\Screen7\\Screen7.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_OPTRAY; //FSM
break;
case 2: //Button 2
display.runScriptFile(F("TPSPORTS\\Screen5\\Screen5.bin")); //load screen
g_state = SERVICE_RTC ; //FSM
break;
case 3: //Button 3
display.runScriptFile(F("TPSPORTS\\Screen6\\Screen6.bin")); //load screen
g_state = SERVICE_CALIBRATION ; //FSM
break;
case 6: //Button 6
display.runScriptFile(F("TPSPORTS\\Screen7\\Screen7.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_OPTRAY; //FSM
break;
default:
break;
} // endswitch
}
/*
* Menu sunglasses selected show the other options
*/
void mainMenuSunglassOptray(){
switch (g_buttonPressed)
{
case 2: //Button Test
g_retest=true;
while(g_retest){
error(F("delete"));
sdc.loadAnyFile(F("testflow/optray.txt")); //the part between / and . ist the folder for storage and must be created in SD-Card.
}
display.runScriptFile(F("TPSPORTS\\Screen7\\Screen7.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_OPTRAY; //FSM
break;
case 3: //Button
display.runScriptFile(F("TPSPORTS\\Screen10\\Screen10.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_CYBER_OPTIC_SHADES; //FSM
break;
case 5: //Button
g_retest=true;
while(g_retest){
g_automatic_run=true;
error(F("delete"));
sdc.loadAnyFile(F("testflow/optray.txt"));
}
g_automatic_run=false;
display.runScriptFile(F("TPSPORTS\\Screen7\\Screen7.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_OPTRAY; //FSM
break;
case 6: //Button
display.runScriptFile(F("TPSPORTS\\Screen8\\Screen8.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_EKOI; //FSM
break;
default:
break;
} // endswitch
}
/*
* Menue ski goggles selected show the other options
*/
void mainMenuSunglassEkoi(){
switch (g_buttonPressed)
{
case 2: //Button Test
g_retest=true;
while(g_retest){
error(F("delete"));
sdc.loadAnyFile(F("testflow/ekoi.txt")); //the part between / and . ist the folder for storage and must be created in SD-Card.
}
display.runScriptFile(F("TPSPORTS\\Screen8\\Screen8.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_EKOI; //FSM
break;
case 3: //Button
display.runScriptFile(F("TPSPORTS\\Screen7\\Screen7.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_OPTRAY; //FSM
break;
case 5: //Button
g_retest=true;
while(g_retest){
g_automatic_run=true;
error(F("delete"));
sdc.loadAnyFile(F("testflow/ekoi.txt"));
}
g_automatic_run=false;
display.runScriptFile(F("TPSPORTS\\Screen8\\Screen8.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_EKOI; //FSM
break;
case 6: //Button
display.runScriptFile(F("TPSPORTS\\Screen10\\Screen10.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_CYBER_OPTIC_SHADES; //FSM
break;
default:
break;
} // endswitch
}
/*
* Menue motorcycle visor show the other options
*/
void mainMenuSunglassCyberOpticShades(){
switch (g_buttonPressed)
{
case 2: //Button Test
g_retest=true;
while(g_retest){
error(F("delete"));
sdc.loadAnyFile(F("testflow/cyberopt.txt")); //the part between / and . ist the folder for storage and must be created in SD-Card.
}
display.runScriptFile(F("TPSPORTS\\Screen10\\Screen10.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_CYBER_OPTIC_SHADES; //FSM
break;
case 3: //Button
display.runScriptFile(F("TPSPORTS\\Screen8\\Screen8.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_EKOI; //FSM
break;
case 5: //Button
g_retest=true;
while(g_retest){
g_automatic_run=true;
error(F("delete"));
sdc.loadAnyFile(F("testflow/cyberopt.txt"));
}
g_automatic_run=false;
display.runScriptFile(F("TPSPORTS\\Screen10\\Screen10.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_CYBER_OPTIC_SHADES; //FSM
break;
case 6: //Button
display.runScriptFile(F("TPSPORTS\\Screen7\\Screen7.bin")); //load screen
g_state = MAIN_MENU_SUNGLASS_OPTRAY; //FSM
break;
default:
break;
} // endswitch
}
/*
* This is the main testflow organizer, there a two states. One, the instruction screen.
* Second, the test screen. Divided in two states and always loaded out of sd-card test structure .txt
*/
int8_t GenericTestFlow(){
bool state = false;
bool end_of_test = false;
bool once =true;
bool once2=true;
do { //catch process until certain conditions are met ex. pressing a certain Button
checkButtonActivity();
switch (state) { //There are two states, one for instruction screen and the other for the acutal testing process
case false: //instruction screen state
if(sdc.generic_instruction_screen.length() > 0){
if(once2){// do it once otherwise it flickers
display.runScriptFile(sdc.generic_instruction_screen);
}
once =true;
once2=false;
}
if (g_buttonPressed == 3) { // Button Back
once=false;
storeDataInSDCard(); //if abort test, store it as far the date is generated
error(F("delete"));
return -1; //abort test
}
if ((g_buttonPressed == 6) || (sdc.generic_instruction_screen.length() == 0)) { //Button Forward or go to next state(test) if there is no instruction screen
//Button forward pressed or no instruction screen available than go to next state
state = true;
once =true;
once2=false;
display.runScriptFile(sdc.generic_test_screen);
}
break;
case true: //state after the instruction screen, the test state
if(once){
runSpecificTest();
testForAutomaticRun();
once =false;
}
if (g_buttonPressed == 3) { // Button Back
once=false;
storeDataInSDCard(); //if abort test store it as far the date is generated
error(F("delete"));
return -1; //abort test
}
if (g_buttonPressed == 5) { //Test end skip forward to next instruction or test screen
once =true;
}
if (g_buttonPressed == 6) { //Test end skip forward to next instruction or test screen
end_of_test = true;
once =false;
}
break;
}
} while (!end_of_test);
}
/*
* Final report "test"
*/
void finalReport(){
if(g_errors[0].length() >0){ //if error text is in variable then write it to screen
display.updateLabel(10, F("Fehler 1:"));
display.updateLabel(20, g_errors[0]);
display.updateLabel(40, extractStoredData(g_errors[0])+'%');
display.runLEDstate(30, LED_RED);
fillInStoreData(1,F("Error_Count")); //store in file that there has an error happened
}else{
display.updateLabel(20, F("Trans_OFF"));
display.updateLabel(21, F("Trans_Summer"));
display.updateLabel(40,extractStoredData(F("Trans_OFF"))+'%');
display.updateLabel(41,extractStoredData(F("Trans_SUMMER"))+'%');
display.runLEDstate(30, LED_GREEN);
}
if(g_errors[1].length() >0){//if error text is in variable then write it to screen
display.updateLabel(11, F("Fehler 2:"));
display.updateLabel(21, g_errors[1]);
display.updateLabel(41, extractStoredData(g_errors[1])+'%');
fillInStoreData(2,F("Error_Count")); //store in file that there has two errors happened
}
if(g_errors[2].length() >0){//if error text is in variable then write it to screen
display.updateLabel(12, F("Fehler 3:"));
display.updateLabel(22, g_errors[2]);
display.updateLabel(42, extractStoredData(g_errors[2])+'%');
fillInStoreData(3,F("Error_Count")); //store in file that there has three errors happened
}
error(F("delete")); //delete all errors logged in variable
}
/*
* Nullabgleich "test" function
*/
void nullabgleich(){
display.runLEDstate(30, LED_ORANGE);
udt.autoRange();
delay(10);
udt.nullabgleich();
delay(10);
g_Transmission0= udt.getMeasure(20);
udt2.autoRange();
delay(10);
udt2.nullabgleich();
delay(10);
g_Transmission0_2= udt2.getMeasure(20);
display.runLEDstate(30, LED_GREEN);
}
/*
* Runs test transmission in off state
*/
void transmissionOff(){
char result[6];
display.runLEDstate(30, LED_ORANGE); // show orange as long as in test
udt.autoRange();
delay(10);
g_TransmissionActual=udt.getMeasure(20);
g_TransmissionPercent=(g_TransmissionActual / g_Transmission0)*100.0; //calculate relative transmission
sprintf(result, "%.2f", g_TransmissionPercent); //makes xx.xx
display.updateLabel(10, result);
if(g_TransmissionPercent<sdc.generic_high_limit&&g_TransmissionPercent>sdc.generic_low_limit){
display.runLEDstate(30, LED_GREEN); //pass
}else{
error(F("Trans_OFF")); //store string with error text in variable
display.runLEDstate(30, LED_RED); //fail
}
fillInStoreData(g_TransmissionPercent);//fill in the result for log file
}
/*
* Runs test transmission in on state
*/
void transmissionON(){
String completeFailText="";
String specificFail="";
char result[6];
display.runLEDstate(30, LED_ORANGE);
if(sdc.generic_lux == F("TWILIGHT")){ //load specific light source itensity
specificFail=F("TWILIGHT");
dl.dl_twilight=sdc.set_lux_to_twilight;
dl.setDl_twilight();
}else if(sdc.generic_lux == F("WINTER")){
specificFail=F("WINTER");
dl.dl_winter=sdc.set_lux_to_winter;
dl.setDl_winter();
}else if(sdc.generic_lux == F("SUMMER")){
specificFail=F("SUMMER");
dl.dl_summer=sdc.set_lux_to_summer;
dl.setDl_summer();
}
delay(1500); //gotta wait cuz its a snail of a light source :)
udt.autoRange();
delay(10);
g_TransmissionActual=udt.getMeasure(20);
g_TransmissionPercent=(g_TransmissionActual / g_Transmission0)*100.0;
sprintf(result, "%.2f", g_TransmissionPercent); //makes xx.xxx
display.TheMatrix::updateLabel(10, result);
dl.setDl_off();
if(g_TransmissionPercent<sdc.generic_high_limit&&g_TransmissionPercent>sdc.generic_low_limit){
display.runLEDstate(30, LED_GREEN); //pass
}else{
completeFailText="Trans_"+specificFail; // fail
error(completeFailText);
display.runLEDstate(30, LED_RED);
}
fillInStoreData(g_TransmissionPercent);
}
/*
* Runs test flicker detection
*/
void flickerDetection(){
}
/*
* Runs test bar detection
*/
void barDetection(){
char result[6];
display.runLEDstate(30, LED_ORANGE);
if(sdc.generic_lux == F("SUMMER")){
dl.dl_summer=sdc.set_lux_to_summer;
dl.setDl_summer();
}
delay(1500); //gotta wait cuz its a snail of a light source :)
udt.autoRange();
delay(10);
g_TransmissionActual_2=udt2.getMeasure(20);
g_TransmissionPercent_2=(g_TransmissionActual_2 / g_Transmission0_2)*100.0;
sprintf(result, "%.2f", g_TransmissionPercent_2); //makes xx.xxx
display.TheMatrix::updateLabel(10, result);
dl.setDl_off();
if(g_TransmissionPercent_2<sdc.generic_high_limit&&g_TransmissionPercent_2>sdc.generic_low_limit){
display.runLEDstate(30, LED_GREEN); //pass
}else{
error(F("Bar detection"));
display.runLEDstate(30, LED_RED);
}
fillInStoreData(g_TransmissionPercent_2);
}
/*
* Error handler function
* Fills error text in array
* Deletes array also
*/
void error(String error){
if(error == F("delete")){
g_errors[0]="";
g_errors[1]="";
g_errors[2]="";
}else{
if(g_errors[0].length() == 0){
g_errors[0]=error;
}else if(g_errors[1].length() == 0){
g_errors[1]=error;
}else if(g_errors[2].length() == 0){
g_errors[2]=error;
}
}
}
/*
* Interrupt which is caused by the scanner
*/
void keyReleased() {
printKey();
}
/*
* analyses the key
*/
void printKey() {
char c = keyboard.getKey(); //ascii-code to char
if (c == '') { //XOFF Pause transmission, das ist kein Leerzeichen, sondern Flowcontrol DC3, Ascii 19
flag_scanner=true;
} else { //just a regular char
g_str_scanner += c;
}
}
/*
* scanner funtion "test" routine
*/
void scanner(){
while(1){ //you have to isolate processor time here in order to get scanner over usb working
checkButtonActivity();
usb.Task();
if (g_buttonPressed == 3) { //release usb.Task
break;
}
if (g_buttonPressed == 6) { //release usb.Task
break;
}
if(flag_scanner){ //this flag is set by printKey() and set the end of transmission, show on display
display.updateLabel(10 , g_str_scanner );
fillInStoreData(g_str_scanner,F("Optrel_ID"));
g_str_scanner="";
flag_scanner=false;
}
}
}
/*
* Loads the result in array for storage/logging
* overloaded function()
*/
void fillInStoreData(float result){
char buf[17];
for(int i=0; i<g_max_row_header;i++){ //circle through header array until current test aka store_name comes up
if(strcmp(g_str_header_for_storage[i][0],sdc.generic_store_name.c_str())==0 ){//if header and store_name matches then store in array position
dtostrf(result,6,2,buf);
for(int j=0;j<17;j++){ //fill buffer in log-array
g_str_header_for_storage[i][1][j]=buf[j];
}
}
}
}
/*
* Loads the result in array for storage/logging
* overloaded function()
*/
void fillInStoreData(float result,String header){
char buf[17];
for(int i=0; i<g_max_row_header;i++){
if(strcmp(g_str_header_for_storage[i][0],header.c_str())==0 ){
dtostrf(result,6,2,buf);
for(int j=0;j<17;j++){
g_str_header_for_storage[i][1][j]=buf[j];
}
}
}
}
/*
* Loads the result in array for storage/logging
* overloaded function()
*/
void fillInStoreData(String data,String header){
char buf[17];
for(int i=0; i<g_max_row_header;i++){
if(strcmp(g_str_header_for_storage[i][0],header.c_str())==0 ){
data.toCharArray(buf,17);
for(int j=0;j<g_max_row_header;j++){
g_str_header_for_storage[i][1][j]=buf[j];
}
}
}
}
/*
* Goes in a array where stored test results are. Takes them out as a string for final report.
*/
String extractStoredData(String header){
String x;
for(int i=0; i<g_max_row_header;i++){
if(strcmp(g_str_header_for_storage[i][0],header.c_str())==0 ){
for(int j=0;j<g_max_row_header;j++){
if(g_str_header_for_storage[i][1][j]=='\0'){
break;
}
x += String(g_str_header_for_storage[i][1][j]);
}
}
}
return x;
}
void testForAutomaticRun(){
if(g_automatic_run==true&&sdc.generic_auto_run=="True"){
g_buttonPressed=6; //simulate a pressed button
delay(750);
}
}
/*
* Out of the generic test flow this runs the specific test
*/
void runSpecificTest() {
if (sdc.scanner) {
scanner();
}
if (sdc.nullabgleich) { //after evaluation of test flow file, checks if its set, then do the test
nullabgleich();
}
if (sdc.flickerDetection) { //after evaluation of test flow file, checks if its set, then do the test
flickerDetection();
}
if (sdc.transmissionOff) { //after evaluation of test flow file, checks if its set, then do the test
transmissionOff();
}
if (sdc.transmissionOn) { //after evaluation of test flow file, checks if its set, then do the test
transmissionON();
}
if (sdc.barDetection){
barDetection();
}
if (sdc.finalReport) { //after evaluation of test flow file, checks if its set, then do the test
finalReport();
storeDataInSDCard();
deleteData();
}
}
int EndsWith(const char *str, const char *suffix)
{
if (!str || !suffix)
return 0;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr)
return 0;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
void deleteData(){
for(int i=0; i<g_max_row_header;i++) {
if(!EndsWith(g_str_header_for_storage[i][0],"_u"))
g_str_header_for_storage[i][1][0] = '\0';// deleting data
}
}
/*
* all the logged data in array will be written in sd-card but before the name has to be constructed
*/
void storeDataInSDCard(){
DateTime now = rtc.now();
String fileName="";
String temp ="";
uint32_t seconds = 0;
fillInStoreData(sdc.testing_instrument_number,F("PruefmittelNR"));//store in log file the Prüfmittelnummer
fillInStoreData(F("TEFPRO"),F("MachineType")); //store in log file the machine type
temp=String(now.year(),10)+"."+String(now.month(),10)+"."+String(now.day(),10);// assemble string date
fillInStoreData(temp,F("TEST_DATE"));//store in log file the date
temp=String(now.hour(),10)+":"+String(now.minute(),10)+":"+String(now.second(),10);
fillInStoreData(temp,F("TEST_TIME"));//store in log file the test time
fileName=sdc.testing_tefpro_number;
seconds=now.year(); //var abuse YYYY
seconds = seconds - 2000;//var abuse YY
temp = String(seconds,36);// 0-Z
if(temp.length()>1){ //year 36 and above
temp.remove(0,1); //cut from XY the X and just print Y
}
fileName = fileName + temp;
fileName = fileName + String(now.month(),36);
fileName = fileName + String(now.day(),36);
seconds = 0;
seconds = 3600*now.hour(); //3600=60*60 hours to seconds conversion
seconds = seconds + 60*now.minute(); //minutes to seconds conversion
seconds = seconds + now.second();
fileName = fileName + String(seconds,36);
sdc.storeData("log",fileName);
sdc.storeData("ftp",fileName); //make a second copy for ftp upload
}
/*
* THE MAIN LOOP of the FSM (finite state machine)
*/
void loop() {
checkButtonActivity();
switch (g_state) {
case WARM_UP_TIMER: warmUpTimer(); break;
case CODE_PAD: codePad(); break;
case MAIN_MENU_SUNGLASS_OPTRAY: mainMenuSunglassOptray(); break;
case MAIN_MENU_SUNGLASS_EKOI: mainMenuSunglassEkoi(); break;
case MAIN_MENU_SUNGLASS_CYBER_OPTIC_SHADES: mainMenuSunglassCyberOpticShades(); break;
case SERVICE_MAIN: serviceMain(); break;
case SERVICE_RTC: serviceRTC(); break;
case SERVICE_CALIBRATION: serviceCalibration(); break;
default : ; break;
}
g_buttonPressed=255;//point to void
}
-----------------------------------------------------------------------------------------------
sd_card.cpp
#include "SD_Card.h"
// FOR INSPECTION TO IMPLEMENTED COUNTING FILES
uint16_t SD_Card::count(String Directory)
{
uint16_t c=0;
File dir = SD.open(Directory);
while (true)
{
File entry = dir.openNextFile();
if (! entry) {
// no more files
break;
}
//Serial.print(entry.name());
if (!(entry.isDirectory()))
{
c=c+1;
}
}
return c;
}
//loads calibartion file into variables
//read testABCD.txt ex. testpano.txt for further insight
void SD_Card::parsCalibratFile(String line)
{
if (line == "") {
line = "NoN NoN";
}
String command;
String parameter;
String productInFile;
int firstSpace = line.indexOf(' ');
command = line.substring(0, firstSpace);
parameter = line.substring(firstSpace + 1);
firstSpace = line.indexOf('_');
productInFile = line.substring(0, firstSpace);
command.trim();
parameter.trim();
if (command == F("TESTING_INSTRUMENT_NUMBER")) {
testing_instrument_number = parameter;
}
if (command == F("TESTING_TEFPRO_NUMBER")) {
testing_tefpro_number = parameter.toInt();
}
if (command == F("SET_LUX_TO_TWILIGHT")) {
set_lux_to_twilight = parameter.toInt();
}
if (command == F("SET_LUX_TO_WINTER")) {
set_lux_to_winter = parameter.toInt();
}
if (command == F("SET_LUX_TO_SUMMER")) {
set_lux_to_summer = parameter.toInt();
}
}
/*
* writes all the logged data in one file in sd-card
* Per test series on file logged, no single big file for all data
*/
void SD_Card::storeData(String path,String fileName) {
int top;
String folderName = path;
String fullPath = {""};
int i = 0;
if(path == "log"){
fullPath = folderName + "/"+folder_name+"/" + fileName + ".csv"; //folder_name is given through load file(testflow/optray.txt) and the name there cutted (optray) out.
}
if(path == "ftp"){
fullPath = folderName + "/" + fileName + ".csv";
}
File myFile = SD.open((fullPath), FILE_WRITE);
while (strcmp(g_str_header_for_storage[i][0],"END")!=0) { //At array [i][0] is the header name stored
myFile.print(g_str_header_for_storage[i][0]);
myFile.print(";");
i++;
}
top=i;
myFile.println("");
for(int i=0; i<top;i++) {
myFile.print(g_str_header_for_storage[i][1]); //At array [i][1] is the testdata stored
myFile.print(";");
}
myFile.close();
}
// stores calibration file, after changing ex. time and steps of day light source
void SD_Card::writeCalibratFile() {
//remove
SD.remove(F("system/calibrat.txt"));
// open a new file and immediately close it:
myFile = SD.open(F("system/calibrat.txt"), FILE_WRITE);
myFile.close();
File myFile = SD.open(F("system/calibrat.txt"), FILE_WRITE); //FILE brauch es...
myFile.print(F("TESTING_INSTRUMENT_NUMBER ")); myFile.println(testing_instrument_number);
myFile.print(F("TESTING_TEFPRO_NUMBER ")); myFile.println(testing_tefpro_number);
myFile.print(F("SET_LUX_TO_TWILIGHT ")); myFile.println(set_lux_to_twilight);
myFile.print(F("SET_LUX_TO_WINTER ")); myFile.println(set_lux_to_winter);
myFile.print(F("SET_LUX_TO_SUMMER ")); myFile.println(set_lux_to_summer);
myFile.close();
}
//opens calibration file
void SD_Card::loadCalibratFile()
{
File dataFile = SD.open("system/calibrat.txt", FILE_READ);
while (dataFile.available())
{
String line = readLineFile(dataFile);
parsCalibratFile(line); //reads lines and fills in variables
}
dataFile.close();
}
//reads line for line in
String SD_Card::readLineFile(File dataFile)
{
char nl = 10;
char cr = 13;
String line = "";
char z;
if (dataFile.available()) {
z = dataFile.read();
}
while (dataFile.available() && z != nl)
{
if (z != nl || z != cr) { //nl= newline cr=carriage return
line = line + z;
}
z = dataFile.read();
}
return line;
}
//Loads test file, makes exp. optray.txt and opens test flow file, parsing through
//It also is defined that the text before .txt is the folder storage name which has to be created in advance(not automated)
void SD_Card::loadAnyFile(String product)
{
int index1 = product.indexOf('/');
int index2 = product.indexOf('.');
folder_name = product.substring(index1+1, index2);
File dataFile = SD.open(product, FILE_READ);
while (dataFile.available()) //read file as long as there is data
{
String line = readLineFile(dataFile);
if (parsAnyFile(line) == -1) { //compare strings from file with certain text which we are looking for
g_retest = false;
break;
}
}
dataFile.close();
}
int8_t SD_Card::parsAnyFile(String line)
{
char* char_ary[30];
if (line == "") {
line = "NoN NoN";
}
String command;
String parameter;
int8_t abort;
int firstSpace = line.indexOf(' ');
int firstSlashOfComment = line.indexOf('/');
command = line.substring(0, firstSpace);
parameter = line.substring(firstSpace + 1, firstSlashOfComment);
command.trim();
parameter.trim();
//***Barcode Scanner***//
if (command == F("SCANNER")) {
if ((parameter == F("End")) && scanner) {
abort = GenericTestFlow();
}
}
if (command == F("SCANNER")) {
scanner = false;
if (parameter == F("Begin")) {
scanner = true;
}
}
if (command == F("SCANNER_TEST")) {
generic_test_screen = parameter;
}
if (command == F("SCANNER_INSTRUCTION")) {
generic_instruction_screen = parameter;
}
if (command == F("SCANNER_STORE_NAME")) {
generic_store_name=(char *)parameter.c_str();
}
if (command == F("SCANNER_AUTO_RUN")) {
generic_auto_run = parameter;
}
//***NULLABGLEICH ***//
if (command == F("NULLABGLEICH")) {
if ((parameter == F("End")) && nullabgleich) {
abort = GenericTestFlow();
}
}
if (command == F("NULLABGLEICH")) {
nullabgleich = false;
if (parameter == F("Begin")) {
nullabgleich = true;
}
}
if (command == F("NULLABGLEICH_INSTRUCTION")) {
generic_instruction_screen = parameter;
}
if (command == F("NULLABGLEICH_TEST")) {
generic_test_screen = parameter;
}
if (command == F("NULLABGLEICH_AUTO_RUN")) {
generic_auto_run = parameter;
}
//***TRANSMISSION OFF***//
if (command == F("TRANSMISSION_OFF")) {
if ((parameter == F("End")) && transmissionOff) {
abort = GenericTestFlow();
}
}
if (command == F("TRANSMISSION_OFF")) {
transmissionOff = false;
if (parameter == F("Begin")) {
transmissionOff = true;
}
}
if (command == F("TRANSMISSION_OFF_INSTRUCTION")) {
generic_instruction_screen = parameter;
}
if (command == F("TRANSMISSION_OFF_TEST")) {
generic_test_screen = parameter;
}
if (command == F("TRANSMISSION_OFF_HIGH_LIMIT")) {
generic_high_limit = parameter.toFloat();
}
if (command == F("TRANSMISSION_OFF_LOW_LIMIT")) {
generic_low_limit = parameter.toFloat();
}
if (command == F("TRANSMISSION_OFF_STORE_NAME")) {
generic_store_name = parameter;
}
if (command == F("TRANSMISSION_OFF_AUTO_RUN")) {
generic_auto_run = parameter;
}
//***FLICKER DETECTION***//
if (command == F("TRANSMISSION_FLICKER")) {
if ((parameter == F("End")) && flickerDetection) {
abort = GenericTestFlow();
}
}
if (command == F("TRANSMISSION_FLICKER")) {
flickerDetection = false;
if (parameter == F("Begin")) {
flickerDetection = true;
}
}
if (command == F("TRANSMISSION_FLICKER_INSTRUCTION")) {
generic_instruction_screen = parameter;
}
if (command == F("TRANSMISSION_FLICKER_TEST")) {
generic_test_screen = parameter;
}
if (command == F("TRANSMISSION_FLICKER_LUX")) {
generic_lux = parameter;
}
if (command == F("TRANSMISSION_FLICKER_STORE_NAME")) {
generic_store_name=(char *)parameter.c_str();
}
//***TRANSMISSION ON***//
if (command == F("TRANSMISSION_ON")) {
if ((parameter == F("End")) && transmissionOn) {
abort = GenericTestFlow();
}
}
if (command == F("TRANSMISSION_ON")) {
transmissionOn = false;
if (parameter == F("Begin")) {
transmissionOn = true;
}
}
if (command == F("TRANSMISSION_ON_INSTRUCTION")) {
generic_instruction_screen = parameter;
}
if (command == F("TRANSMISSION_ON_TEST")) {
generic_test_screen = parameter;
}
if (command == F("TRANSMISSION_ON_LUX")) {
generic_lux = parameter;
}
if (command == F("TRANSMISSION_ON_HIGH_LIMIT")) {
generic_high_limit = parameter.toFloat();
}
if (command == F("TRANSMISSION_ON_LOW_LIMIT")) {
generic_low_limit = parameter.toFloat();
}
if (command == F("TRANSMISSION_ON_STORE_NAME")) {
generic_store_name=(char *)parameter.c_str();
}
if (command == F("TRANSMISSION_ON_AUTO_RUN")) {
generic_auto_run = parameter;
}
//*** BAR DETECTION***//
if (command == F("BAR_DETECTION")) {
if ((parameter == F("End")) && barDetection) {
abort = GenericTestFlow();
}
}
if (command == F("BAR_DETECTION")) {
barDetection = false;
if (parameter == F("Begin")) {
barDetection = true;
}
}
if (command == F("BAR_DETECTION_INSTRUCTION")) {
generic_instruction_screen = parameter;
}
if (command == F("BAR_DETECTION_TEST")) {
generic_test_screen = parameter;
}
if (command == F("BAR_DETECTION_ON_LUX")) {
generic_lux = parameter;
}
if (command == F("BAR_DETECTION_HIGH_LIMIT")) {
generic_high_limit = parameter.toFloat();
}
if (command == F("BAR_DETECTION_LOW_LIMIT")) {
generic_low_limit = parameter.toFloat();
}
if (command == F("BAR_DETECTION_STORE_NAME")) {
generic_store_name=(char *)parameter.c_str();
}
if (command == F("BAR_DETECTION_AUTO_RUN")) {
generic_auto_run = parameter;
}
//***FINAL REPORT ***//
if (command == F("FINAL_REPORT")) {
if ((parameter == F("End")) && finalReport) {
abort = GenericTestFlow();
}
}
if (command == F("FINAL_REPORT")) {
finalReport = false;
if (parameter == F("Begin")) {
finalReport = true;
}
}
if (command == F("FINAL_REPORT_INSTRUCTION")) {
generic_instruction_screen = parameter;
}
if (command == F("FINAL_REPORT_SCREEN")) {
generic_test_screen = parameter;
}
if (command == F("FINAL_REPORT_AUTO_RUN")) {
generic_auto_run = parameter;
}
if (abort == -1) {
return -1;
}
}
------------------------------------------------------------------------------------------------
sd_card.h
//#include <SD.h>
#include <SPI.h>
#include <SD.h>
#ifndef _SD_Card_h
#define _SD_Card_h
extern int8_t GenericTestFlow();
extern bool g_retest;
extern char g_str_header_for_storage[15][2][17];
class SD_Card
{
protected:
File myFile;
//Hilfsfunktionen
String readLineFile(File dataFile);
int8_t parsAnyFile(String line);
void parsCalibratFile(String line);
public:
uint16_t count(String Directory);
void loadCalibratFile();
void writeCalibratFile();
void loadAnyFile(String product);
void storeData(String path,String fileName);
//generic var
String folder_name;
String generic_auto_run;
String generic_store_name;
String generic_lux;
String generic_instruction_screen;
String generic_test_screen;
String testing_instrument_number;
uint16_t testing_tefpro_number;
uint16_t set_lux_to_twilight;
uint16_t set_lux_to_winter;
uint16_t set_lux_to_summer;
float generic_high_limit;
float generic_low_limit;
bool scanner;
bool nullabgleich;
bool transmissionOff;
bool transmissionOn;
bool finalReport;
bool flickerDetection;
bool barDetection;
};
#endif
-----------------------------------------------------------------------------------------------
udt_tramp.cpp
/*
Name: UDT_TRAMPLib.cpp
Created: 28.09.2016 10:28:47
Author: mala
Editor: http://www.visualmicro.com
*/
#include "UDT_TRAMP.h"
#include <math.h>
void UDT_TRAMP::begin()
{
begin(0);
}
void UDT_TRAMP::begin(uint8_t adrr_mcp23008)
{
_mcp.begin(adrr_mcp23008);
_ads.begin();
_mcp.pinMode(0, OUTPUT); //Range 3
_mcp.pinMode(1, OUTPUT); //Range 2
_mcp.pinMode(2, OUTPUT); //Range 1
_mcp.pinMode(7, INPUT); //OVLD
_mcp.digitalWrite(0, LOW);
_mcp.digitalWrite(1, LOW);
_mcp.digitalWrite(2, LOW);
_exponent = 3;
}
//Overload, udt reached OVLD, which means that analog voltage is over a certain output level and it is necessary to switch to an other amplification factor
bool UDT_TRAMP::getOVLD()
{
return !_mcp.digitalRead(7);
}
//set udt to a certain amplification factor
void UDT_TRAMP::setRange(uint8_t r)
{
if (r < 3) { r = 3; }
if (r > 10) { r = 10; }
_exponent = r;
switch (r)
{
case 3: _mcp.digitalWrite(2, LOW); _mcp.digitalWrite(1, LOW); _mcp.digitalWrite(0, LOW); break;
case 4: _mcp.digitalWrite(2, HIGH); _mcp.digitalWrite(1, LOW); _mcp.digitalWrite(0, LOW); break;
case 5: _mcp.digitalWrite(2, LOW); _mcp.digitalWrite(1, HIGH); _mcp.digitalWrite(0, LOW); break;
case 6: _mcp.digitalWrite(2, HIGH); _mcp.digitalWrite(1, HIGH); _mcp.digitalWrite(0, LOW); break;
case 7: _mcp.digitalWrite(2, LOW); _mcp.digitalWrite(1, LOW); _mcp.digitalWrite(0, HIGH); break;
case 8: _mcp.digitalWrite(2, HIGH); _mcp.digitalWrite(1, LOW); _mcp.digitalWrite(0, HIGH); break;
case 9: _mcp.digitalWrite(2, LOW); _mcp.digitalWrite(1, HIGH); _mcp.digitalWrite(0, HIGH); break;
case 10: _mcp.digitalWrite(2, HIGH); _mcp.digitalWrite(1, HIGH); _mcp.digitalWrite(0, HIGH); break;
}
}
//read analoge voltage and set range according to incoming voltage in a optimal range
void UDT_TRAMP::autoRange()
{
bool zuKlein = false;
double mv = getMiliVolt(5);
//Wenn weniger als 0.5V gemessen wird soll die Verstaerkung um 10 erhoeht werden
if (mv < 500.0) { zuKlein = true; }
// Ebenfalls soll die Verstaerkung erhoeht werden, wenn mehr als 10V (bei ganz kleinen Spannungen macht der ADC einen Feheler und gibt nicht 0 sondern 65534 aus) gemessen werden aber kein Overload ausgegeben wird
if ((mv > 10000.0 && !getOVLD())) { /*Serial.println("ADC_Error -> Gain++");*/ zuKlein = true; }
while (zuKlein)
{
zuKlein = false;
mv = getMiliVolt(5);
if (mv < 500.0) { zuKlein = true; }
if (mv > 10000.0 && !getOVLD()) { /*Serial.println("ADC_Error -> Gain++");*/ zuKlein = true; }
setRange(_exponent + 1);
if(_exponent==10){ zuKlein = false; } //Verstaerkung kann nicht weiter erhaerht werden -> Abbruch
delay(100);
}
while (getOVLD() && (_exponent > 3)) {
setRange(_exponent - 1);
delay(100);
}
}
//returns current range
uint8_t UDT_TRAMP::getRange()
{
return _exponent;
}
//reads ADC
uint16_t UDT_TRAMP::getADC()
{
_ads.readADC_SingleEnded(0);
delay(10);
return _ads.readADC_SingleEnded(0);
}
//makes a certain amount of measurements of ADC in ADC steps
double UDT_TRAMP::getADC(uint8_t anzahlMessungen)
{
_ads.readADC_SingleEnded(0);
delay(10);
int32_t summe = 0;
for (int i = 0; i < anzahlMessungen; i++) { summe = summe + _ads.readADC_SingleEnded(0); delay(10); }//100 didnt help
double r = (double)summe / (double)anzahlMessungen;
return r;
}
//makes a certain amount of measurements of ADC in miliVolt
double UDT_TRAMP::getMiliVolt(uint8_t anzahlMessungen)
{
double mV = getADC(anzahlMessungen);
mV = mV*0.1875;
return mV;
}
//calculates out of ADC value the measured value
double UDT_TRAMP::getMeasure()
{
double resultat;
resultat = getADC()*(pow(10, (7 - _exponent)));
//resultat = getADC()*(pow(10, (7 - (_exponent-3))));
//resultat = getADC()*(pow(10, (_exponent-3)));
return resultat;
}
double UDT_TRAMP::getMeasure(uint8_t anzahlMessungen)
{
double r = 0;
for (int i = 0; i < anzahlMessungen; i++) {
r = r + getMeasure();
}
r=r/ anzahlMessungen;
return r;
}
//-----------------------------//
// SST Calculation //
//-----------------------------//
void UDT_TRAMP::nullabgleich()
{
_nullabgleich = getMeasure();
}
//calculates out of the ADC the SST
double UDT_TRAMP::getSST()
{
double sst = 0;
uint64_t val = 0;
autoRange();
if (_nullabgleich != 0)
{
val = getMeasure();
double l0 = log10(_nullabgleich);
double l = log10(val);
double ld = l - l0;
double vsst = (ld * 7) / 3;
sst = 1 - vsst;
}
sst+=0.063; //Korrekturfaktor von Mess- Kalkulationsfehler TEFLAB
return sst;
}
//calculates out of the ADC the SST
double UDT_TRAMP::getSST(uint8_t anzahlMessungen)
{
double sst = 0;
double val = 0;
autoRange();
if (_nullabgleich != 0)
{
val = getMeasure(anzahlMessungen);
double l0 = log10(_nullabgleich);
double l = log10(val);
double ld = l - l0;
double vsst = (ld * 7) / 3;
sst = 1 - vsst;
}
sst+=0.063; //Korrekturfaktor von Mess- Kalkulationsfehler TEFLAB
return sst;
}
------------------------------------------------------------------------------------------------
udt_tramp.h
/*
Name: UDT_TRAMPLib.h
Created: 20201006
Author: dasc
Major change is; uint64_t is replaced with double. Because >12 SST we got sever precision issues. The resolution wasnt float, more like steps, so we lost information.
based on library:
Name: UDT_TRAMPLib.h
Created: 28.09.2016 10:28:47
Author: mala
*/
//#define ADS1015_ADDRESS (0x48)
#define ADS1015_ADDRESS2 (0x49)
#ifndef _UDT_TRAMPLib_h
#define _UDT_TRAMPLib_h
#include <Wire.h>
#include <Adafruit_ADS1015.h>
#include <Adafruit_MCP23008.h>
#include <math.h>
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
class UDT_TRAMP
{
private:
Adafruit_MCP23008 _mcp;
Adafruit_ADS1115 _ads;
uint8_t _exponent;
double _nullabgleich;
public:
UDT_TRAMP(uint8_t addr = ADS1015_ADDRESS): _ads{addr}
{
// Constructor
}
void begin();
void begin(uint8_t adrr_mcp23008);
bool getOVLD();
void setRange(uint8_t r);
uint8_t getRange();
double getMeasure(); //used to be uint64
double getMeasure(uint8_t anzahlMessungen);
void autoRange();
uint16_t getADC();
double getADC(uint8_t anahlMessungen);
double getMiliVolt(uint8_t anzahlMessungen);
void nullabgleich();
double getSST();
double getSST(uint8_t anzahlMessungen);
};
#endif
------------------------------------------------------------------------------------------------
lightsource.cpp
/*
Name: lightsource.cpp
Created: xx.01.2021
Author: DASC
*/
#include <Arduino.h>
#include "lightsource.h"
void Light_Source::init(void)
{
Serial3.begin(9600);
}
void Light_Source::setDl_off()
{
setDl_value(0);
}
void Light_Source::setDl_twilight()
{
setDl_value(dl_twilight);
}
void Light_Source::setDl_winter()
{
setDl_value(dl_winter);
}
void Light_Source::setDl_summer()
{
setDl_value(dl_summer);
}
void Light_Source::setDl_value(uint16_t j)
{
String command;
if(j>4095){j==4095;}
command = "SET " + String(j, DEC);
Serial3.println(command);
}
------------------------------------------------------------------------------------------------
lightsource.h
/*
Name: light source
Created: xx.01.2021
Author: DASC
*/
class Light_Source
{
protected:
public:
void init(void);
uint16_t dl_twilight;
uint16_t dl_winter;
uint16_t dl_summer;
void setDl_off();
void setDl_twilight();
void setDl_winter();
void setDl_summer();
void setDl_value(uint16_t newVal);
};
------------------------------------------------------------------------------------------------
main.h
#include <Wire.h>
#include "SD_Card.h"
#include "timer.h"
#include "matrix.h"
#include <stdio.h>
#include <string.h>
#include "lightsource.h"
#include "UDT_TRAMP.h"
#include <avr/dtostrf.h>
// ethernet module
#include <Ethernet2.h>
#include <Dns.h>
//usb scanner
#include <KeyboardController.h>
//BUTTONS AND PINS
#define BUTTON1 22 //Button "CANCEL"
#define BUTTON2 23 //Button "OK"
#define BUTTON3 24 //Button "LEFT"
#define BUTTON4 25 //Button "UP"
#define BUTTON5 26 //Button "DOWN"
#define BUTTON6 27 //Button "RIGHT"
//FUNCTION PROTOTYPES
void checkButtonActivity();
void start();
void warmUpTimer();
void codePad();
void clearCode();
void serviceMain();
void serviceRTC();
void serviceCalibration();
void mainMenuSunglassOptray();
void mainMenuSunglassCyberOpticShades();
void mainMenuSunglassEkoi();
void updateIndices();
int8_t GenericTestFlow();
void runSpecificTest();
void testForAutomaticRun();
void finalReport();
void nullabgleich();
void transmissionOff();
void transmissionON();
void flickerDetection();
void barDetection();
void error(String error);
void scanner();
void keyReleased();
void printKey();
String extractStoredData(String header);
void storeData();
void fillInStoreData(float result);
void fillInStoreData(float result,String header);
void fillInStoreData(String data,String header);
void storeDataInSDCard();
byte uploadDataToFTP();
byte eRcv();
void deleteData();
int EndsWith(const char *str, const char *suffix); //capital letters because its reserved
//OBJECT
Timer g_warmUpTimer;
//GLOBAL VARIABLES
unsigned long g_startMillis_locked_repress;
unsigned long g_currentMillis_locked_repress;
const unsigned long g_period_locked_repress=1000; //delay between pressing is 1000ms fast than that will be thrown away.
char g_outBuf[128];
bool g_flag_ethernet=true;
char g_input_code[4];
int g_printable_chars;
int g_input_index{0};
byte g_buttonPressed{255}; //stores the code/value of pressed button
byte g_state{255}; // stores the current state of state machine
bool g_retest;
bool g_automatic_run;
String g_errors[3];
bool flag_scanner=false;
String g_str_scanner="";
byte mac[] = { // MAC of the module, is given, labeled
0xA8, 0x61, 0x0A, 0xAE, 0x70, 0x4F
};
uint8_t g_max_row_header=15; //count g_str_header_for_storage type the number here
//Add new positions at the end, before {"END","END"}! It's the only way to go for excel power query
char g_str_header_for_storage[15][2][17]={
{"PruefmittelNR",""},
{"MachineType",""},
{"TEST_DATE",""},
{"TEST_TIME",""},
{"Hans_ID",""},
{"Trans_OFF",""},
{"Trans_OFF_u","pct"},
{"Trans_TWILIGHT",""},
{"Trans_TWILIGHT_u","pct"},
{"Trans_WINTER",""},
{"Trans_WINTER_u","pct"},
{"Trans_SUMMER",""},
{"Trans_SUMMER_u","pct"},
{"Error_Count",""},
{"END","END"}
};
//Stores 0/90degree Transmission from Tramp
double g_Transmission0;
double g_TransmissionActual;
double g_TransmissionPercent;
//Stores 32degree Transmission from Tramp_2
double g_Transmission0_2;
double g_TransmissionActual_2;
double g_TransmissionPercent_2;
//##LOCAL DEBUG FTP SERVER ADDRESS
//const char* dns_name_of_server="172.16.3.107"; //type in the address of ftp server for debug upload
//const String ftp_user="USER any";
//const String ftp_pw="PASS any";
enum LED
{
LED_OFF,
LED_GREEN,
LED_ORANGE,
LED_RED,
};
enum State{
START,
WARM_UP_TIMER,
CODE_PAD,
SERVICE_MAIN,
SERVICE_RTC,
SERVICE_CALIBRATION,
MAIN_MENU_SUNGLASS_OPTRAY,
MAIN_MENU_SUNGLASS_EKOI,
MAIN_MENU_SUNGLASS_CYBER_OPTIC_SHADES
};
------------------------------------------------------------------------------------------------
/*
* Author: RIEN / DASC
* Created: 13.03.2019
* Version: 1.1
* Last update: 10.04.2019
* This library contains the functions for the Matrix Screen and the i2c functions needed to control it.
*/
#include "matrix.h"
#include <Wire.h>
#define ASCII_Format 0 //Define ASCII label format
#define SPAM_INTERRUPT 20 // Prevent Matrix Screen from crashing when data is spammed to updateLabel(). 10 ms is already Stable!
#define I2C_Address 0x28 //Define default 8bit I2C address of 0x50 >> 1 for 7bit Arduino
// This function activates the buzzer.
void TheMatrix::activateBuzzer(const short frequency, const short duration)
{
char command[] = { 254, 187, (byte)(frequency >> 8), (byte)(frequency), (byte)(duration >> 8), (byte)(duration)};
i2cWrite(command, sizeof(command));
}
/* This funtion reads the input (touch) from the Matrix screen using i2c-communications. The position touched
* on the screen is determined by an ID, that is defined in the GTT Software.
* The function returns ID 255 if the data read is not 252 and then 135 (in that order).
* ID 255 has been defined as "do nothing" by the screen developers. They return -1 to an unsigned integer,
* which overflows to 255 on the Arduino DUE.
*/
byte TheMatrix::getID()
{
byte data{i2cRead()}; //Read the command prefix
// Serial.print("data0:");Serial.println(data);
if (data == 252) // Magic number!
{
//Serial.print("data1:");Serial.println(data);
data = i2cRead(); //Read the touch prefix
// Serial.print("data2:");Serial.println(data);
if (data == 135) // Magic number!
{
int message_length{i2cRead() << 8 | i2cRead()}; //Read mesage length
byte event{i2cRead()}; //Read event type
byte id{i2cRead()}; //Read region that generated the touch response
//Serial.print("getID(), id: "); Serial.println(id);
return id;
}
}
return 255; // Reserved ID for automatic break.
}
/*This function reads one byte over i2c
*/
byte TheMatrix::i2cRead()
{
// Serial.println("inI2cRead");
do
{
Wire1.requestFrom(I2C_Address, 1);
} while (Wire1.available() < 1);
// Serial.println("outI2cRead");
return (Wire1.read());
}
/*This function writes an array of bytes (C-style string) over i2c
*/
void TheMatrix::i2cWrite(const char *data, const byte data_length)
{
Wire1.beginTransmission(I2C_Address);
for (int i = 0; i < data_length; i++)
Wire1.write(data[i]);
Wire1.endTransmission();
}
/* ???
*/
void TheMatrix::runLEDstate(const byte index, const byte state) { //whole screen
char command[] = { 254, 195, index, state};
i2cWrite(command, sizeof(command));
}
/* These functions write image-files to the Matrix screen in the form of backgrounds.
*/
void TheMatrix::runScriptFile(const String &s) {
//Serial.print("inRunScriptFileString&s:"); Serial.println(s);
delay(50); // Soll verhindern wenn Button gelesen wird und sofort geschrieben wird, dass Display nicht abstürtz. Etwa Faktor 2x als sicherheit.
char command[] = { 254, 93};
i2cWrite(command, sizeof(command));
int len = s.length();
char arr[len + 1];
s.toCharArray(arr, len + 1);
i2cWrite(arr, len);
char terminator[] = {0};
i2cWrite(terminator, 1);
}
void TheMatrix::runScriptFile(const char *message, const byte messageLength) { //whole screen
char command[] = { 254, 93};
i2cWrite(command, sizeof(command));
i2cWrite(message, messageLength);
char terminator[] = {0};
i2cWrite(terminator, 1);
}
void TheMatrix::runScriptFile(const __FlashStringHelper *ifsh) {
delay(50); // Soll verhindern wenn Button gelesen wird und sofort geschrieben wird, dass Display nicht abstürtz. Etwa Faktor 2x als sicherheit.
char command[] = { 254, 93};
i2cWrite(command, sizeof(command));
PGM_P p = reinterpret_cast<PGM_P>(ifsh);
PGM_P p2 = reinterpret_cast<PGM_P>(ifsh);
size_t n = 0;
int len = 0;
while (1) {
unsigned char d = pgm_read_byte(p2++);
len++;
if (d == 0) break;
}
char arr[len];
int x = 0;
while (1) {
unsigned char c = pgm_read_byte(p++);
arr[x] = c;
x++;
if (c == 0) break;
}
i2cWrite(arr, len - 1);
char terminator[] = {0};
i2cWrite(terminator, 1);
}
/* This function sets the label font colour.
*/
void TheMatrix::setLabelFontColour(const byte index, const byte red, const byte green, const byte blue)
{
char command[] = { 254, 21, index, red, green, blue};
i2cWrite(command, sizeof(command));
//Response will be handled by loop()
}
/* These functions write a message into a label on the Matrix Screen.
* Index is the ID of the label? The message to be shown on the label is passed into the
* function as a string. Because i2cWrite() can only process C-style string, the message
* is converted into one before it is passed to i2cWrite(). A null terminator is sent after
* the end of the new C-style string.
*/
void TheMatrix::updateLabel(const byte index, const String &message, const byte message_length)
{
char command[] = { 254, 17, index, ASCII_Format }; // Magic Numbers!
i2cWrite(command, sizeof(command));
int len = message.length() + 1;
char c_message[len];
message.toCharArray(c_message, len);
i2cWrite(c_message, message_length);
char terminator[] = { 0 };
i2cWrite(terminator, 1);
delay(SPAM_INTERRUPT);
}
void TheMatrix::updateLabel(const byte index, const char *c)
{
char command[] = { 254, 17, index, 0 };
i2cWrite(command, sizeof(command));
int len = strlen(c);
i2cWrite(c, len);
char terminator[] = { 0 };
i2cWrite(terminator, 1);
delay(SPAM_INTERRUPT);
}
//untested function
void TheMatrix::updateLabel(const byte index, const String &s)
{
char command[] = { 254, 17, index, 0 };
i2cWrite(command, sizeof(command));
int len = s.length();
char arr[len + 1];
s.toCharArray(arr, len + 1);
i2cWrite(arr, len);
char terminator[] = { 0 };
i2cWrite(terminator, 1);
delay(SPAM_INTERRUPT);
}
void TheMatrix::updateLabel(const byte index, const uint32_t n)
{
char buffer [33];
byte size;
char command[] = { 254, 17, index, 0 };
i2cWrite(command, sizeof(command));
itoa(n, buffer, 10);
int len = strlen(buffer);
i2cWrite(buffer, len);
char terminator[] = { 0 };
i2cWrite(terminator, 1);
delay(SPAM_INTERRUPT);
}
void TheMatrix::updateLabel(const byte index, const __FlashStringHelper *ifsh)
{
char command[] = { 254, 17, index, 0 };
i2cWrite(command, sizeof(command));
PGM_P p = reinterpret_cast<PGM_P>(ifsh);
PGM_P p2 = reinterpret_cast<PGM_P>(ifsh);
size_t n = 0;
int len = 0;
while (1)
{
unsigned char d = pgm_read_byte(p2++);
len++;
if (d == 0) break;
}
char arr[len];
int x = 0;
while (1)
{
unsigned char c = pgm_read_byte(p++);
arr[x] = c;
x++;
if (c == 0) break;
}
i2cWrite(arr, len - 1);
char terminator[] = { 0 };
i2cWrite(terminator, 1);
delay(SPAM_INTERRUPT);
}
------------------------------------------------------------------------------------------------
matrix.h
/*
* Author: RIEN
* Created: 13.03.2019
* Version: 1.1
* Last update: 10.04.2019
* Header file for the Matrix Screen (matrix.cpp) and its funtions.
*/
#ifndef MATRIX_H
#define MATRIX_H
#include <Wire.h>
/* TheMatrix is a Monostate and therefor does not need to be defined!
* This can be changed if the need for 2 Matrix screens on the same device ever arises.
*/
class TheMatrix
{
public:
static void activateBuzzer(const short frequency, const short duration);
static byte getID();
static void runLEDstate(const byte index, const byte state);
static void runScriptFile(const char *message, const byte messageLength);
static void runScriptFile(const __FlashStringHelper *ifsh);
static void runScriptFile(const String &s);
static void setLabelFontColour(const byte index, const byte red, const byte green, const byte blue);
static void updateLabel(const byte index, const String &message, const byte message_length);
static void updateLabel(const byte index, const char *c);
//static void updateLabel(byte index, char c[]); //changed to pointer
static void updateLabel(const byte index, const String &s);
static void updateLabel(const byte index, const uint32_t n);
static void updateLabel(const byte index, const __FlashStringHelper *ifsh);
private:
static byte i2cRead();
static void i2cWrite(const char *data, const byte data_length);
};
#endif // MATRIX_H
------------------------------------------------------------------------------------------------
timer.cpp
/*
* Author: RIEN
* Created: 03.05.2019
* Version: 1.0
* Last update: -
* This library contains the functions for the Timers.
*/
#include "timer.h"
#include "Arduino.h"
#include <RTClib.h>
////////////////////////////////////////////////////////////////////////////////
// Timer implementation
// Constructor for passing a timespan variable.
Timer::Timer(const TimeSpan &timeSpan) : m_duration{timeSpan}
{
//
}
// Constructor for passing time values directly
Timer::Timer(int16_t days, int8_t hours, int8_t minutes, int8_t seconds)
: m_duration{days, hours, minutes, seconds}
{
//
}
// Copy Constructor
Timer::Timer(const Timer ©) : m_duration{copy.m_duration}
{
//
}
/* Updates the Timer to the time remaining.
*/
void Timer::updateTimer()
{
m_timer = m_timerEnd - RTC_PCF8523::now();
if (m_timer.totalseconds() <= 0)
m_timerDone = true;
}
// Warning: Because this function uses a piece of Hardware (the RTC) it has to be executed in or after setup()!
void Timer::resetTimer()
{
m_timerDone = false;
m_timerEnd = RTC_PCF8523::now() + m_duration;
}
int32_t Timer::getRemainingSeconds()
{
if (m_timerDone)
return -1;
updateTimer();
return (m_timer.totalseconds() >= 0 ? m_timer.totalseconds() : -1);
}
int8_t Timer::getRemainingMinutes()
{
if (m_timerDone)
return -1;
updateTimer();
return (m_timer.minutes() >= 0 ? m_timer.minutes() : -1);
}
-------------------------------------------------------------------------------------------------
timer.h
/*
* Author: RIEN
* Created: 13.05.2019
* Version: 1.0
* Last update: -
* Header file for Timers (timer.cpp) and its funtions.
*/
#ifndef TIMER_H
#define TIMER_H
#include "Arduino.h"
#include <RTClib.h>
// Class for all timers, e.g. warmup, recalibration, ect.
// A new timer needs to be defined before it can be used. The default timer duration is 30 minutes.
class Timer
{
protected:
const TimeSpan m_duration;
DateTime m_timerEnd{0};
TimeSpan m_timer;
bool m_timerDone{true};
private:
void updateTimer();
public:
// Constructor for passing a timespan variable. Default 30 minutes
Timer(const TimeSpan &timeSpan = TimeSpan{0, 0, 10, 0});
// Constructor for passing time values directly
Timer(const int16_t days, const int8_t hours, const int8_t minutes, const int8_t seconds);
// Copy Constructor
Timer(const Timer ©);
const TimeSpan& getDuration() { return m_duration; }
void resetTimer();
//In order to prevent overflow after a long time span, these get-functions are a bit more complex.
int32_t getRemainingSeconds();
int8_t getRemainingMinutes();
}; // end class Timer
#endif
-------------------------------------------------------------------------------------------------