Build a standalone GPS based speed logger and throw in lean angle too.. because.. MOTORBIKE!
Achieved.
WIP, write data to sdcard as a KML, 90%
Objective2: WIP, hack into the CANBus via OBD to extract info like RPM, Gear, etc directly from bike ECU, initial attempts quite successful. WIP 50%
UPDATE: CANBUS ARDUINO feed added, the data ported to my gopro
The build hardware:
1. Arduino UNO. - LINK- Price: AUD $11.
(but if you can spring for it and have space, go Mega2560 to bypass the irritation of setting up software serial. if not, DO NOT USE standard software serial, its sucks donkey bum..)
2. USBasp programmer -LINK- Price AUD $4.
If you use an UNO or Nano, with only one serial port, you will have the ovious issue of it not connecting to the pc via RX/TX because the GPS or Screen will use that, so you need to write directly via an AVR programmer.
3. UBlox6 GPS module -LINK- Price AUD $8
if you spend a few $ more you can buy them with header presoldered. By default these will spit out UBX protocol,and the arduino library available for that woudl not compile, so download the free uCentre app for UBlox, and follow the manual instructions for setting it to NMEA output, you can disable all GPS sentence except for GGA and RMC, set the update frequency to 10hz, and baud rate to 115200, this will allow you to take valid reading in 20ms or less, critical as a delay here messes with accelerometer readings.
4. 32PTU Display -LINK- Price AUD $61
5. GyroScope -LINK- Price AUD $2 - $10
(Any accelerometer will do, I would have preferred a 9DOF unit but jaycar don't sell them anymore, the unit i used is analog with no jitter control, so up to you,just change pins in the code to suit)
As all Arduino based projects, the libraries at your disposal will be poorly documented, the hardware will likely be upscaled from 3.3 or actually still at 3.3v, be carefull, upscaled analog means you have to throw readings above 530 (1~1024) out the window.. allocate in your code if you go analog.
I have my own 3dprinters, and plan to print in ABS once I have finalised the design in Rhino, but you can buy several units from jaycar that will house it for under $5 each.
Code Notes:
I scrapped the screen reset pin from my design, as its not required, I setup the screen and the arduino to boot to my splash screen, wait the 5 seconds it takes for comms to go live then dump intothe main display.
In the Visi side of thing the various buttons are coded into the Visi builder so that the AR side is not bogged down reading button push status update, increase in speed is significant. The 4dsystems visi and 4Ds language is quite easy to learn if you are already au faix with C#, but has a few kinks to learn.
Visi Screens:
Arduino Side Code:
//hardware // MEGA 2560 // UBX6 gps on serial2, configured via Ucentre for: // NMEA @ 115200, 10hz and ONLY rmc and gga sentences // uLCD 220rd on serial1, configured in 4d WS @9600 // Accelerometer MMA7361, Y axis on pin A9 #include <genieArduino.h> #include <TinyGPS++.h> #include <AltSoftSerial.h> #define RESETLINE 5 #define GPSPOWER 2 TinyGPSPlus gps; Genie genie; // Board TX RX PWM Unusable // ----- -------- ------- ------------ // Arduino Uno 9 8 10 AltSoftSerial Display; unsigned long writeKMLold = 0, writeKMLCurrent = 0; const int jittercorrect = 20; //poll period for bump averaging float gpsLatitude, gpsLongitude; double Speed, speedMax; int AccY = A0, leanMax = 0, leanCurrent = 0, gpsLock = 0; int gpsDirection = 0, gpsAltitude = 0; int currentForm = 0; bool header = false, record = true; void setup() { Display.begin(9600); genie.Begin(Display); //pinMode(RESETLINE, OUTPUT); //digitalWrite(RESETLINE, 1); delay(100); //digitalWrite(RESETLINE, 0); delay (35000); //power the gps pinMode(GPSPOWER, OUTPUT); digitalWrite(GPSPOWER, HIGH); genie.AttachEventHandler(checkScreen); Serial.begin(115200); delay(3500); SetForm(0); } void loop() { genie.DoEvents(); gpsUpdate(); genie.DoEvents(); readAcc(); genie.DoEvents(); updateMax(); } void updateMax() { int LeanMath = leanCurrent; if (LeanMath < 0)LeanMath *= -1; if (LeanMath > leanMax)leanMax = LeanMath; if (Speed > speedMax)speedMax = Speed; switch (currentForm) { case 0: genie.WriteObject(GENIE_OBJ_LED_DIGITS, 3, (int)speedMax); genie.WriteObject(GENIE_OBJ_LED_DIGITS, 4, leanMax); break; case 1: genie.WriteObject(GENIE_OBJ_LED_DIGITS, 6, (int)speedMax); break; case 2: genie.WriteObject(GENIE_OBJ_LED_DIGITS, 2, (int)leanMax); break; } } void gpsUpdate() { unsigned long start = millis(); do { while (Serial.available())gps.encode(Serial.read()); } while (millis() - start < 25); //grab 20ms of gps info Speed = gps.speed.kmph(); if (Speed > 2)Speed = 0; gpsDirection = (int)gps.course.deg(); gpsLatitude = gps.location.lat(); gpsLongitude = gps.location.lng(); gpsLock = gps.satellites.value(); gpsAltitude = (int)gps.altitude.meters(); if (gpsLock > 2) { switch (currentForm) { case 0: genie.WriteObject(GENIE_OBJ_COOL_GAUGE, 0, (int)Speed); genie.WriteObject(GENIE_OBJ_GAUGE, 5, gpsLock); break; case 3: genie.WriteStr(0, String(gpsLatitude, 6)); genie.WriteStr(1, String(gpsLongitude, 6)); genie.WriteStr(2, gpsAltitude); genie.WriteObject(GENIE_OBJ_GAUGE, 4, gpsLock); break; } } } void readAcc() { // RAW: L = 200 C = 385 R = 545 int jitter = 0; for (int a = 0; a < 5; a++) { jitter += map(analogRead(AccY), 200, 545, -90, 90); delay(jittercorrect); } leanCurrent = (int)jitter / 5; //poll a few times and average if (leanCurrent < -90)leanCurrent = -90; if (leanCurrent > 90)leanCurrent = 90; switch (currentForm) { case 0: if (leanCurrent < 0) { int lean = leanCurrent *= -1; genie.WriteObject(GENIE_OBJ_GAUGE, 2, lean); genie.WriteObject(GENIE_OBJ_GAUGE, 3, 0); } else { genie.WriteObject(GENIE_OBJ_GAUGE, 2, 0); genie.WriteObject(GENIE_OBJ_GAUGE, 3, leanCurrent); } break; case 2: if (leanCurrent < 0) { int lean = leanCurrent *= -1; genie.WriteObject(GENIE_OBJ_GAUGE, 0, lean); genie.WriteObject(GENIE_OBJ_GAUGE, 1, 0); genie.WriteObject(GENIE_OBJ_LED_DIGITS, 0, lean); genie.WriteObject(GENIE_OBJ_LED_DIGITS, 1, 0); } else { genie.WriteObject(GENIE_OBJ_GAUGE, 0, 0); genie.WriteObject(GENIE_OBJ_GAUGE, 1, leanCurrent); genie.WriteObject(GENIE_OBJ_LED_DIGITS, 0, 0); genie.WriteObject(GENIE_OBJ_LED_DIGITS, 1, leanCurrent); } break; } } void checkScreen() { genieFrame Event; genie.DequeueEvent(&Event); if (Event.reportObject.cmd == GENIE_REPORT_EVENT) { if (Event.reportObject.object == GENIE_OBJ_FORM) { currentForm = (int)Event.reportObject.index; } if (Event.reportObject.object == GENIE_OBJ_WINBUTTON) { int pressed = (int)Event.reportObject.index; switch (pressed) { case 25: record = true; break; case 26: record = false; break; case 27: leanMax = 0; break; case 28: speedMax = 0; break; } } } } void SetForm(int form) { currentForm = form; genie.WriteObject(GENIE_OBJ_FORM, form, 0); } void StartKMLWrite() { if (header == false) { if (gpsLock > 4) //String filename1(gps.time.value()); //String filename = filename1 + ".kml"; //filename.toCharArray(fileNameChar, 13); //open the file on SD //write the header info writeHeaderKML("Teststring.kml"); //replace with filenamechar above header = true; } } void UpdateKML(long interval) { writeKMLCurrent = millis(); if (writeKMLCurrent - writeKMLold >= interval) { writeKMLold = writeKMLCurrent; writeRecordKML(Speed, leanCurrent, gpsAltitude, gpsLatitude, gpsLongitude); } } void writeHeaderKML(String Filename) { //myFile = SD.open(fileNameChar, FILE_WRITE); Serial.println (F("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")); Serial.println (F("<kml xmlns=\"http://earth.google.com/kml/2.1\">")); Serial.println (F("<Document>")); Serial.print (F("<name>")); Serial.print(Filename); Serial.println(F("</name>")); Serial.println (F("<visibility>1</visibility>")); Serial.print (F("<description>")); Serial.print(F("CeeBee Datalogger")); Serial.println(F("</description>")); //myFile.close(); } void writeFooterKML() { //myFile = SD.open(fileNameChar, FILE_WRITE); Serial.println (F("</Document>")); Serial.println (F("</kml>")); //myFile.close(); } void writeRecordKML(int Speed, int LeanAngle, int Altitude, float Latitude, float Longitude) { //myFile = SD.open(fileNameChar, FILE_WRITE); Serial.println (F(" <Placemark>")); Serial.print (F(" <name>")); Serial.print(Speed); Serial.println(F("</name>")); Serial.println (F(" <ExtendedData>")); Serial.println (F(" <Data name=\"Lean Angle Degrees\">")); Serial.print (F(" <value>")); Serial.print(LeanAngle); Serial.println(F("</value>")); Serial.println (F(" </Data>")); Serial.println (F(" <Data name=\"Speed KMPH\">")); Serial.print (F(" <value>")); Serial.print(Speed); Serial.println(F("</value>")); Serial.println (F(" </Data>")); Serial.println (F(" </ExtendedData>")); Serial.println (F(" <Point>")); Serial.print (F(" <coordinates>")); Serial.print (Longitude, 6); Serial.print(F(",")); Serial.print (Latitude, 6); Serial.print(F(",")); Serial.print (Altitude, 6); Serial.println (F("</coordinates>")); Serial.println (F(" </Point>")); Serial.println (F(" </Placemark>")); //myFile.close(); }
platform "uLCD-220RD" // generated 12/05/2018 3:23:00 PM #MODE FLASHBANK_0 #inherit "4DGL_16bitColours.fnc" #inherit "VisualConst.inc" #inherit "visicodedConst.inc" #inherit "CLPrintStrings.inc" #constant IPDatasize 22 #CONST CMDLenMAX 80 seroutX $serout serinX $serin #END #CONST ColorBGimage 0x0020 ACK 0x06 NAK 0x15 ReadCmd 0x80 WriteCmd 0x00 // IPD_TYPE 0 // offsets are doubled as FLASH is byte addressable Ofs_IPD_P1 2 Ofs_IPD_P2 4 Ofs_IPD_P3 6 Ofs_IPD_P4 8 Ofs_IPD_P5 10 Ofs_IPD_P6 12 Ofs_IPD_P7 14 Ofs_IPD_DOWN 16 Ofs_IPD_RELEASE 18 Ofs_IPD_OBJVIDX 20 // object indexes tDipSwitch 0 tKnob 1 tRockerSwitch 2 tRotarySwitch 3 tGSlider 4 tTrackbar 5 tWinButton 6 tAngularmeter 7 // need to implement use of this, inputs must be ordered first tCoolgauge 8 tCustomdigits 9 tForm 10 tGauge 11 tImage 12 tKeyboard 13 // this is a special input, does not need to be at front tLed 14 tLeddigits 15 tMeter 16 tStrings 17 // also need output strings code // tStringUNI 0x3f | 0x40 // tStringANSII 0x3f tThermometer 18 tUserled 19 tVideo 20 tStaticText 21 // Remove, check for non visual objects instead // MaxVisObjects 21 // objects that have a visual component tSounds 22 tTimer 23 tSpectrum 24 tScope 25 tTank 26 tUserImages 27 tPinOutput 28 tPinInput 29 t4Dbutton 30 // more inputs tAniButton 31 tColorPicker 32 tUserButton 33 tMagicObject 34 MaxTotObjects 33 // objects in objects array // OT_DISPLAY 22 OT_REPORT 100 OT_SETCONST 101 OT_SETANOTHER 102 OT_ACTIVATE 103 OT_NEXTFRAME 104 OT_PREVFRAME 105 OT_NEXTSTRING 106 OT_PREVSTRING 107 OT_MAGIC 108 // other OT_s Form activate, // Indexes into LedDigits and CustomDigits arrays Ofs_Digits_Left 0 Ofs_Digits_Digits 2 Ofs_Digits_MinDigits 4 Ofs_Digits_Widthdigit 6 Ofs_Digits_LeadingBlanks 8 // indexes to Strings arrays Ofs_String_StartH 0 Ofs_String_StartL 2 Ofs_String_Size 4 Ofs_String_x1 6 Ofs_String_y1 8 Ofs_String_x2 10 Ofs_String_y2 12 Ofs_String_FGColor 14 Ofs_String_BGColor 16 Ofs_String_FontAttribs 18 Ofs_String_Transparent 20 // bit transparent should 'refresh' background, otherwise rectangle out Ofs_String_Ansi 22 // bit defines write/draw routine Ofs_String_Form 24 // form this string can be seen in // Command codes READ_OBJ 0 WRITE_OBJ 1 WRITE_STR 2 WRITE_STRU 3 WRITE_CONTRAST 4 REPORT_OBJ 5 REPORT_EVENT 7 WRITE_MAGIC_BYTES 8 WRITE_MAGIC_DBYTES 9 REPORT_MAGIC_EVENT_BYTES 10 REPORT_MAGIC_EVENT_DBYTES 11 // End P1.inc nObjects 10 nInputs 0 nAniTimers 0 nStrings 6 #END #DATA word FormStartIndex 0 word FormEndIndex 10 word InputControls 0 word InputData 0 word iStrings0 Strings0StartH, Strings0StartL, Strings0Size, 112, 193, 162, 206, YELLOW, BLACK, 0, 1, 1, 0 word iStrings1 Strings1StartH, Strings1StartL, Strings1Size, 68, 132, 86, 144, YELLOW, BLACK, 0, 1, 1, 0 word iStrings2 Strings2StartH, Strings2StartL, Strings2Size, 65, 148, 86, 159, YELLOW, BLACK, 0, 1, 1, 0 word iStrings3 Strings3StartH, Strings3StartL, Strings3Size, 54, 162, 86, 172, YELLOW, BLACK, 0, 1, 1, 0 word iStrings4 Strings4StartH, Strings4StartL, Strings4Size, 61, 176, 86, 187, YELLOW, BLACK, 0, 1, 1, 0 word iStrings5 Strings5StartH, Strings5StartL, Strings5Size, 72, 10, 144, 22, YELLOW, BLACK, 0, 1, 1, 0 word oDipSwitchs 0 word oKnobs 0 word oRockerSwitchs 0 word oRotarySwitchs 0 word oGSliders 0 word oTrackbars 0 word oWinButtons 0 word oAngularmeters 1, iAngularmeter0 word oCoolgauges 1, iCoolgauge0 word oCustomdigitss 0 word oForms 1, -1 word oGauges 0 word oImages 0 word oKeyboards 0 word oLeds 0 word oLeddigitss 1, iLeddigits0 word oMeters 0 word oStringss 6, iStrings0, iStrings1, iStrings2, iStrings3, iStrings4, iStrings5 word oThermometers 0 word oUserleds 1, iUserled0 word oVideos 0 word oStaticTexts 5, iStatictext0, iStatictext1, iStatictext2, iStatictext3, iStatictext4 word oSpectrums 0 word oScopes 0 word oTanks 0 word oUserImagess 0 word oPinInputs 0 word o4Dbuttons 0 word oAniButtons 0 word oColorPickers 0 word oUserButtons 0 word oMagicObjects 0 word oSmartGauges 0 word oSmartSliders 0 word oSmartKnobs 0 word oTimers 0 word oSoundss 0 word oPinOutputs 0 word FormBGcolors 0x0000 word kKeyboardKeystrokes -1 word rKeyboardRoutines -1 word oLedDigitsn 92, 3, 1, 35, 1 #END var hFonts[6] ; var stringsCV[6] := [0, 0, 0, 0, 0, 0], hstrings ; // Start P2.inc var oObjects[MaxTotObjects+1] ; // address of objects var CurrentForm, oldn, ImageTouched ; var TouchXpos, TouchYpos ; var InputType, TouchState, CurInputData, pInputIndex ; var comRX[40], cmd[CMDLenMAX] ; var InputCS, OutputCS ; func seroutCS(var op) serout(op) ; OutputCS ^= op ; endfunc func nak0() serout(NAK) ; InputCS := 0 ; endfunc func seroutOcs() serout(OutputCS) ; OutputCS := 0 ; endfunc func SendReport(var id, var objt, var objn, var val) seroutCS(id) ; seroutCS(objt) ; seroutCS(objn) ; seroutCS(val >> 8) ; // first 8 bits seroutCS(val) ; seroutOcs() ; endfunc func ReadObject(var ObjectType, var ObjectIdx) var j, k, Objects ; Objects := *(oObjects+ObjectType) ; j := 2 + ObjectIdx * 2 + Objects ; if (ObjectType == tForm) k := CurrentForm ; else if ((ObjectType == tCustomdigits) || (ObjectType == tLeddigits)) k := img_GetWord(hndl, *j, IMAGE_TAG2); else if (ObjectType == tStrings) k := stringsCV[ObjectIdx]; else k := img_GetWord(hndl, *j, IMAGE_INDEX); endif SendReport(REPORT_OBJ, ObjectType, ObjectIdx, k) ; endfunc func WriteObject(var ObjectType, var ObjectIdx, var NewVal) var i, j, k, Objects ; ObjectType &= 0x3f ; if (ObjectType == tForm) ActivateForm(ObjectIdx) ; else Objects := *(oObjects+ObjectType)+ObjectIdx*2+2 ; i := *(Objects) ; if (ObjectType == tLeddigits) img_SetWord(hndl, i , IMAGE_TAG2, NewVal); // where state is 0 to 2 ledDigitsDisplay(i, oLeddigitss, oLedDigitsn) ; else if (ObjectType == tStrings) PrintStrings(ObjectIdx, NewVal, 0); else img_SetWord(hndl, i , IMAGE_INDEX, NewVal); // where state is 0 to 2 img_Show(hndl, i) ; // will only display if form is current endif endif endfunc // WARNING, this code will crash if newval exceeds maximum displayable number func ledDigitsDisplay(var imgidx, var typeptr, var setptr) var i, j, k, l, lb, newval, num[4] ; if (!((img_GetWord(hndl, imgidx, IMAGE_FLAGS) & I_ENABLED))) return ; // ;img_GetWord(hndl, imgidx, IMAGE_TAG2) ;if diabled then exit newval := img_GetWord(hndl, imgidx, IMAGE_TAG2) ; i := -1 ; j := *(typeptr) ; repeat typeptr += 2 ; i++ ; until (*(typeptr) == imgidx); j := setptr + i*10 ; l := 0x500a | (*(j+Ofs_Digits_Digits) << 8) ; // UDECxZ to(num) ; putnum(l, newval) ; imgidx++ ; lb := *(j+Ofs_Digits_LeadingBlanks) ; l := str_Ptr(num) ; for (i := 0; i < *(j+Ofs_Digits_Digits); i++) k := str_GetByte(l++) & 0x0f ; if ( lb && (i < *(j+Ofs_Digits_Digits) - *(j+Ofs_Digits_MinDigits)) ) if (k == 0) k := 10 ; else lb := 0 ; endif endif img_SetWord(hndl, imgidx, IMAGE_INDEX, k); img_SetWord(hndl, imgidx, IMAGE_XPOS, *(j+Ofs_Digits_Left)+i* *(j+Ofs_Digits_Widthdigit)) ; img_Show(hndl, imgidx); next endfunc func ActivateForm(var newform) var i, j, *p ; if (CurrentForm != -1) // deactivate old form, by disabling all inputs for (i := FormStartIndex[CurrentForm]; i <= FormEndIndex[CurrentForm]; i++) if (img_GetWord(hndl, i, IMAGE_TAG)) img_Disable(hndl,i) ; endif next endif CurrentForm := newform ; // display newform image or clear to image color if (FormBGcolors[CurrentForm] != ColorBGimage) gfx_Set(BACKGROUND_COLOUR,FormBGcolors[CurrentForm]); gfx_Cls() ; DoGFXObjects() ; // display GFX 'widgets' endif // enable inputs for (i := FormStartIndex[CurrentForm]; i < FormEndIndex[CurrentForm]; i++) j := img_GetWord(hndl, i, IMAGE_TAG) ; if (j) j-- ; img_SetAttributes(hndl, i, I_STAYONTOP+I_ENABLED); // make sure this is on top of form, if applicable //if (j != tKeyboard) if ((j <= tWinButton) || (j >= t4Dbutton) ) // enable inputs img_ClearAttributes(hndl, i, I_TOUCH_DISABLE); // ensure touch is enabled endif img_Show(hndl,i) ; // show initialy, if required if (j == tForm) DoGFXObjects() ; // display GFX 'widgets' for image backgruobds else if (j == tLeddigits) ledDigitsDisplay(i, oLeddigitss, oLedDigitsn) ; endif endif next for (i := 0; i < nStrings; i++) if (stringsCV[i] != -1) WriteObject(tStrings, i, stringsCV[i]) ; endif next endfunc func UpdateObjects(var newval) var IPidx, otherOBJ ; if ( ( img_GetWord(hndl, *(pInputIndex), IMAGE_INDEX) != newval) || (TouchState == Ofs_IPD_RELEASE) ) // only bother if values changed, or release img_SetWord(hndl, *(pInputIndex), IMAGE_INDEX, newval); img_Show(hndl, *(pInputIndex)); // only shows on current form IPidx := *(CurInputData+TouchState) ; while(IPidx != 0) otherOBJ := IPidx + InputData; if (*(otherOBJ) == OT_REPORT) SendReport(REPORT_EVENT, InputType, *(otherOBJ+Ofs_IPD_OBJVIDX), newval) ; else if (*(otherOBJ) == OT_MAGIC) IPidx := *(otherOBJ+Ofs_IPD_P5) ; IPidx(newval) ; else if (TouchState == *(otherOBJ+Ofs_IPD_P4)) if (*(otherOBJ) == OT_ACTIVATE) ActivateForm(*(otherOBJ+Ofs_IPD_P2) ) ; InputType := tForm ; else if (*(otherOBJ) == OT_SETCONST) newval := *(otherOBJ+Ofs_IPD_P3) ; WriteObject(*(otherOBJ+Ofs_IPD_P1), *(otherOBJ+Ofs_IPD_P2), newval) ; else if (*(otherOBJ) == OT_SETANOTHER) WriteObject(*(otherOBJ+Ofs_IPD_P1), *(otherOBJ+Ofs_IPD_P2), newval) ; else if (*(otherOBJ) == OT_PREVFRAME) if (img_GetWord(hndl, *(otherOBJ+Ofs_IPD_P6), IMAGE_INDEX)) WriteObject(*(otherOBJ+Ofs_IPD_P5),*(otherOBJ+Ofs_IPD_P2),img_GetWord(hndl, *(otherOBJ+Ofs_IPD_P6), IMAGE_INDEX)-1) ; endif newval := img_GetWord(hndl, *(otherOBJ+Ofs_IPD_P6), IMAGE_INDEX) ; else if (*(otherOBJ) == OT_NEXTFRAME) if (img_GetWord(hndl, *(otherOBJ+Ofs_IPD_P6), IMAGE_INDEX) < *(otherOBJ+Ofs_IPD_P3)) WriteObject(*(otherOBJ+Ofs_IPD_P5),*(otherOBJ+Ofs_IPD_P2),img_GetWord(hndl, *(otherOBJ+Ofs_IPD_P6), IMAGE_INDEX)+1) ; endif newval := img_GetWord(hndl, *(otherOBJ+Ofs_IPD_P6), IMAGE_INDEX) ; else if (*(otherOBJ) == OT_PREVSTRING) if (stringsCV[*(otherOBJ+Ofs_IPD_P2)]) WriteObject(tStrings,*(otherOBJ+Ofs_IPD_P2),stringsCV[*(otherOBJ+Ofs_IPD_P2)]-1) ; endif else if (*(otherOBJ) == OT_NEXTSTRING) if (stringsCV[*(otherOBJ+Ofs_IPD_P2)] < *(otherOBJ+Ofs_IPD_P3)) // fix IPD_P2 not filled in yet WriteObject(tStrings,*(otherOBJ+Ofs_IPD_P2),stringsCV[*(otherOBJ+Ofs_IPD_P2)]+1) ; endif endif endif IPidx := *(otherOBJ+TouchState) ; wend endif endfunc // End P2.inc func DoGFXObjects() endfunc // Start P3.inc func main() var comTX[50], cmdi, i, j, TouchStatus ; gfx_ScreenMode(PORTRAIT) ; putstr("Mounting...\n"); if (!(file_Mount())) while(!(file_Mount())) putstr("Drive not mounted..."); pause(200); gfx_Cls(); pause(200); wend endif // gfx_MoveTo(0, 0); // print(mem_Heap()," ") ; // gfx_TransparentColour(0x0020); // gfx_Transparency(ON); // open image control hndl := file_LoadImageControl("VISICO~1.dat", "VISICO~1.gci", 1); // init 'constants' // End P3.inc oObjects[tDipSwitch] := oDipSwitchs ; oObjects[tKnob] := oKnobs ; oObjects[tRockerSwitch] := oRockerSwitchs ; oObjects[tRotarySwitch] := oRotarySwitchs ; oObjects[tGSlider] := oGSliders ; oObjects[tTrackbar] := oTrackbars ; oObjects[tWinButton] := oWinButtons ; oObjects[tAngularmeter] := oAngularmeters ; oObjects[tCoolgauge] := oCoolgauges ; oObjects[tCustomdigits] := oCustomdigitss ; oObjects[tForm] := oForms ; oObjects[tGauge] := oGauges ; oObjects[tImage] := oImages ; oObjects[tKeyboard] := oKeyboards ; oObjects[tLed] := oLeds ; oObjects[tLeddigits] := oLeddigitss ; oObjects[tMeter] := oMeters ; oObjects[tStrings] := oStringss ; oObjects[tThermometer] := oThermometers ; oObjects[tUserled] := oUserleds ; oObjects[tVideo] := oVideos ; oObjects[tStaticText] := oStaticTexts ; oObjects[tSounds] := oSoundss ; oObjects[tTimer] := oTimers ; oObjects[tSpectrum] := oSpectrums ; oObjects[tTank] := oTanks ; oObjects[tUserImages] := oUserImagess ; oObjects[tPinOutput] := oPinOutputs ; oObjects[tPinInput] := oPinInputs ; oObjects[t4Dbutton] := o4Dbuttons ; oObjects[tAniButton] := oAniButtons ; oObjects[tColorPicker] := oColorPickers ; oObjects[tUserButton] := oUserButtons ; hFonts[0] := FONT3 ; hFonts[1] := FONT3 ; hFonts[2] := FONT3 ; hFonts[3] := FONT3 ; hFonts[4] := FONT3 ; hFonts[5] := FONT3 ; // Start P4.inc hstrings := file_Open("VISICO~1.txf", 'r') ; // Open handle to access uSD strings, uncomment if required // init comms com_Init(comRX,CMDLenMAX,0); com_SetBaud(COM0,960); com_TXbuffer(comTX, 100, 0); // tag 'real' objects for (i := 0; i <= MaxTotObjects; i++) if ( (i != tSounds) && (i != tTimer) && (i != tPinOutput) && (i != tPinInput) ) TouchXpos := oObjects[i] ; TouchYpos := *(TouchXpos) ; for (ImageTouched := 1; ImageTouched <= TouchYpos; ImageTouched++) oldn := *(TouchXpos+ImageTouched*2) ; img_SetAttributes(hndl, oldn, I_TOUCH_DISABLE); // ensure touch is enabled if (oldn != -1) img_SetWord(hndl, oldn, IMAGE_TAG, i+1); img_Disable(hndl, oldn) ; endif next endif next // display initial form CurrentForm := -1 ; // End P4.inc // Start P5.inc ActivateForm(0) ; // need to change this according to first actual form // End P5.inc // Start P6.inc touch_Set(TOUCH_ENABLE); // enable the touch screen oldn := -1 ; repeat // check comms for command, how to NAK invalid command if (com_Count() != 0) i := serin() ; InputCS ^= i ; // update checksum if ( (cmdi > 2) && (cmd[0] == WRITE_STRU) ) j := (cmdi-1) >> 1 + 2 ; if (j == CMDLenMAX) // max length exceeded nak0() ; cmdi := -1 ; else if (cmdi & 1) cmd[j] := i ; if (cmd[2] == 0) // if string complete if (InputCS) nak0() ; else if (cmd[0] == WRITE_STRU) cmd[j] := 0 ; // terminate it PrintStrings(cmd[1], &cmd[3], 1) ; serout(ACK) ; else endif endif cmdi := -1 ; endif else cmd[j] := cmd[j] << 8 + i ; cmd[2]-- ; // dec length endif cmdi++ ; else // not unicode string cmd[cmdi++] := i ; if (cmd[0] == WRITE_STR) // Ansi String if (cmdi == CMDLenMAX) // max length exceeded nak0() ; cmdi := 0 ; else if (cmdi > 2) if (cmd[2] == -1) if (InputCS) nak0() ; else if (cmd[0] == WRITE_STR) cmd[cmdi-1] := 0 ; // terminate it PrintStrings(cmd[1], &cmd[3], 1) ; serout(ACK) ; else endif endif cmdi := 0 ; else cmd[2]-- ; // dec length endif endif else if ( (cmd[0] == READ_OBJ) && (cmdi == 4) ) if (InputCS) nak0() ; else ReadObject(cmd[1], cmd[2]) ; endif cmdi := 0 ; else if ( (cmd[0] == WRITE_OBJ) // 6 byte write command (gen option) && (cmdi == 6) ) if (InputCS) nak0() ; else WriteObject(cmd[1], cmd[2], cmd[3] << 8 + cmd[4]) ; serout(ACK) ; endif cmdi := 0 ; else if ( (cmd[0] == WRITE_CONTRAST) && (cmdi == 3) ) if (InputCS) nak0() ; else gfx_Contrast(cmd[1]) ; serout(ACK) ; endif cmdi := 0 ; else if (cmdi == 6) // we have 6 bytes and we've gotten here -> something wrong nak0() ; cmdi := 0 ; endif endif // not unicode string endif // a character is available // touch code processing TouchStatus := touch_Get(TOUCH_STATUS); // get touchscreen status ImageTouched := img_Touched(hndl,-1) ; if ((TouchStatus == TOUCH_PRESSED) || (TouchStatus == TOUCH_RELEASED) || (TouchStatus == TOUCH_MOVING)) if ((TouchStatus != TOUCH_RELEASED) && (ImageTouched != oldn) && (oldn != -1)) TouchStatus := TOUCH_RELEASED ; // simulate release if we move off object endif if (TouchStatus != TOUCH_RELEASED) // if not released if (oldn != -1) ImageTouched := oldn ; else if (oldn != ImageTouched) oldn := ImageTouched ; TouchStatus := TOUCH_PRESSED ; endif endif TouchXpos := touch_Get(TOUCH_GETX); TouchYpos := touch_Get(TOUCH_GETY); TouchState := Ofs_IPD_DOWN ; else ImageTouched := oldn ; // simulate release of what we touched oldn := -1 ; // prevent double release TouchState := Ofs_IPD_RELEASE ; endif if (ImageTouched != -1) CurInputData := InputControls[ImageTouched] + InputData; InputType := *(CurInputData) ; i := InputType ; if (InputType >= t4Dbutton) i -= 23 ; // adjust to ensure next in gosub gosub (i), (cDipswitch, cKnob, cRockerswitch, cRotaryswitch, cSlider, cTrackbar, cWinbutton, c4DButton, cAniButton, cColorPicker, cUserButton) ; endif endif // if ((n != -1) && (oldn == -1)) oldn := n ; // save what we touched in case we move off it sys_EventsResume() ; forever cDipswitch: cKnob: cRockerswitch: cRotaryswitch: cSlider: cTrackbar: c4DButton: cUserButton: cWinbutton: gbutton: cAniButton: cColorPicker: endfunc // End P6.inc