Components and supplies
Positioning (GNSS, GPS, Glonass, Galileo, Beidou)
Arduino Ethernet Shield 2
Perspex Sheet
DS3231 I2C RTC
LED Matrix I2C interface
IP65 Enclosure
Resistor 1k ohm
10 DOF gyro / accelerometer board
Buck Power Supply
HC-05 Bluetooth Module
Arduino Mega 2560
DUAL H-Bridge motor drive
ESP8266 ESP-12E
Capacitor 100 nF
Resistor 2.21k ohm
Cable Gland
Tools and machines
Hole Punch or Hole saw
Hand Drill
Soldering iron (generic)
Apps and platforms
Arduino IDE
Project description
Code
TRACKER_MEGA_TOP_GPS_PWM_SOFT.ino
c_cpp
Tracker for Ethernet shield uses both GPS and NTP to retrieve time
1#include <SFE_BMP180.h> 2#include <avr/wdt.h> 3#include <Wire.h> 4#include <SPI.h> 5#include <LSM303.h> // modified ... fixed a couple of bugs 6#include <LiquidCrystal_I2C.h> 7#include <L3G.h> 8#include "ds3231.h" 9#include <TimeLib.h> 10#include "ht16k33.h" 11#include <ModbusRtu.h> // Modified ... this no longer a stock lib - Slave supports register translation/mapping 12#include <EEPROM.h> 13#include <math.h> 14#include <Ethernet.h> 15#include <SD.h> 16#include <EthernetUdp.h> 17#include <TinyGPS.h> 18 19#define ID 1 20 21#define BUFF_MAX 32 22#define PARK_EAST 1 23#define PARK_WEST 2 24#define PARK_NORTH 3 25#define PARK_SOUTH 4 26#define PARK_FLAT 5 27 28#define MOTOR_DWELL 100 29 30#define MAX_MODBUS_DATA 70 31 32#define HT16K33_DSP_NOBLINK 0 // constants for the half arsed cheapo display 33#define HT16K33_DSP_BLINK1HZ 4 34#define HT16K33_DSP_BLINK2HZ 2 35#define HT16K33_DSP_BLINK05HZ 6 36 37 38const byte ENCODER_PINA = 2; // encoder connects - interupt pin 39const byte ENCODER_PINB = 3; // non interupt pin 40const byte ENCODER_PB = 8; 41 42const byte RELAY_YZ_DIR = 8; // DIR 1 Y+ Y- East / West Was the X+ N relay BROWN 43const byte RELAY_YZ_PWM = 7; // PWM 2 Speed East / West Was the Y- E relay ORANGE 44const byte RELAY_XZ_PWM = 6; // PWM 2 Speed North / South Was the Y+ W relay YELLOW 45const byte RELAY_XZ_DIR = 5; // DIR 1 X+ X- North / South Was the X- S relay BLUE 46 47 48const int chipSelect = 4; 49 50const byte UNUSED09 = 9; // cycle timer 26 Hz 38ms period 51const byte UNUSED10 = 10; 52const byte UNUSED11 = 11; 53const byte WATCHDOG = 12; 54 55IPAddress ip(192,168,2,177) ; 56IPAddress MyDNS(139,130,4,4) ; 57unsigned int localPort = 8888; // local port to listen for UDP packets 58EthernetServer server(80) ; 59EthernetClient client ; 60 61byte mac[] = { 0x44, 0x6f, 0x75, 0x67, 0x61, 0x6c }; // Dougal in ASCII 62char timeServer[] = "au.pool.ntp.org"; // time.nist.gov NTP server 63const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message 64byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets 65 66 67EthernetUDP Udp; // A UDP instance to let us send and receive packets over UDP 68 69static bool hasSD = false; 70static bool hasNet = false; 71static bool hasGyro = false; 72static bool hasRTC = false; 73static bool hasPres = false ; 74 75L3G gyro; 76LSM303 compass; 77LiquidCrystal_I2C lcd(0x27); // Set the LCD I2C address I modified the lib for default wiring 78 79SFE_BMP180 pressure; 80HT16K33 HT; 81 82TinyGPS gps; 83 84float ha ; 85float sunX ; 86float sunrise ; 87float sunset ; 88Modbus mb_slave(ID, 2, 0); // this is slave ID and RS-232 or USB-FTDI 89time_t chiptime ; 90uint8_t rtc_status ; 91//uint8_t time[8]; 92int motor_recycle = 0 ; 93char recv[BUFF_MAX]; 94unsigned int recv_size = 0; 95unsigned long prev_millis; 96uint8_t u8state; // machine state 97uint8_t u8query; // pointer to message query 98char buff[BUFF_MAX]; 99char trackername[18] ; 100unsigned long gpschars ; 101float heading ; // MODBUS MAP 102struct ts t; // 103struct ts td; // 104struct ts tg; // 105struct ts tc; // 106int iNightShutdown ; // 107time_t setchiptime ; // 68 if set to non zero this will trigger a time set event 108float zAng ; // 66 109float xMul = 1.0 ; // 64 110float yMul = 1.0 ; // 62 111float zMul = 1.0 ; // 60 112int iXYS = 0 ; // 59 113int iSave = 0 ; // 58 114int iDoSave = 0 ; // 57 115int iGPSLock = 0 ; // 56 116unsigned long fixage ; // 54 117float xRoll = 0.0 ; // 52 118float yRoll = 0.0 ; // 50 119float zRoll = 0.0 ; // 48 120float gT ; // 46 temp from sensor 121float Pr ; // 44 presure sensor 122float alt ; // 42 altitude from GPS 123float T; // 40 temperature of board (if has RTC) 124float xzTarget ; // 38 target for angles 125float yzTarget ; // 36 126float xzH ; // 34 hyserisis zone 127float yzH ; // 32 128float xzAng; // 30 current angles 129float yzAng; // 28 130float xzOffset; // 26 offset xz 131float yzOffset; // 24 offset yz 132float dyPark; // 22 parking position 133float dxPark; // 20 134float xMinVal ; // 18 Min and Max values X - N/S 135float xMaxVal ; // 16 136float yMinVal ; // 14 Y -- E/W 137float yMaxVal ; // 12 138float latitude; // 10 139float longitude; // 8 140int timezone; // 7 141int iDayNight ; // 6 142float solar_az_deg; // 4 143float solar_el_deg; // 2 144int iTrackMode ; // 1 145int iMode ; // 0 146 147int iPMode; 148int iPWM_YZ ; 149int iPWM_XZ ; 150int iPowerUp = 0 ; 151 152unsigned long tempus; 153int8_t state1 = 0; 154int8_t rtc_hour = 0; 155int8_t rtc_min = 0 ; 156int8_t rtc_sec = 0 ; 157 158void LoadParamsFromEEPROM(bool bLoad){ 159 if ( bLoad ) { 160 xzH = LoadFloatFromEEPROM(0,0.1,20.0,4.0); // hysterisis NS 161 yzH = LoadFloatFromEEPROM(1,0.1,20.0,4.0); // "" EW 162 163 dyPark = LoadFloatFromEEPROM(2,-70.0,50.0,0); 164 dxPark = LoadFloatFromEEPROM(3,-5.0,50.0,0.0); 165 166 xzOffset = LoadFloatFromEEPROM(4,-90.0,90.0,0); // NS 167 yzOffset = LoadFloatFromEEPROM(5,-90.0,90.0,0); // EW 168 169 xzTarget = LoadFloatFromEEPROM(6,-90.0,90.0,0); // NS 170 yzTarget = LoadFloatFromEEPROM(7,-90.0,90.0,0); // EW 171 172 xMinVal = LoadFloatFromEEPROM(8,-10.0,60.0,0.0); // NS 173 xMaxVal = LoadFloatFromEEPROM(9,-10.0,60.0,45); 174 175 yMinVal = LoadFloatFromEEPROM(10,-70.0,50.0,-65); // EW 176 yMaxVal = LoadFloatFromEEPROM(11,-70.0,50.0,45); 177 178 iTrackMode = LoadIntFromEEPROM(12,-1,4,0); 179 180 latitude = LoadFloatFromEEPROM(13,-90.0,90.0,-34.051219); 181 longitude = LoadFloatFromEEPROM(14,-180.0,180.0,142.013618); 182 timezone = LoadIntFromEEPROM(15,0,23,10); 183 xMul = LoadFloatFromEEPROM(16,-10,10,1); 184 yMul = LoadFloatFromEEPROM(17,-10,10,1); 185 zMul = LoadFloatFromEEPROM(18,-10,10,1); 186 iXYS = LoadIntFromEEPROM(19,0,1,0); 187 if ( xMul == 0.0 ) // zero is rubbish value so take 1.0 as the default 188 xMul = 1.0 ; 189 if ( yMul == 0.0 ) 190 yMul = 1.0 ; 191 if ( zMul == 0.0 ) 192 zMul = 1.0 ; 193 iNightShutdown = LoadIntFromEEPROM(20,0,1,1); 194 EEPROM.get(0 + (30 * sizeof(float)) , trackername ); 195 }else{ 196 EEPROM.put( 0 , xzH ); 197 EEPROM.put(0 + (1 * sizeof(float)) , yzH ); 198 EEPROM.put(0 + (2 * sizeof(float)) , dyPark ); 199 EEPROM.put(0 + (3 * sizeof(float)) , dxPark ); 200 EEPROM.put(0 + (4 * sizeof(float)) , xzOffset ); 201 EEPROM.put(0 + (5 * sizeof(float)) , yzOffset ); 202 EEPROM.put(0 + (6 * sizeof(float)) , xzTarget ); 203 EEPROM.put(0 + (7 * sizeof(float)) , yzTarget ); 204 EEPROM.put(0 + (8 * sizeof(float)) , xMinVal ); 205 EEPROM.put(0 + (9 * sizeof(float)) , xMaxVal ); 206 EEPROM.put(0 + (10 * sizeof(float)) , yMinVal ); 207 EEPROM.put(0 + (11 * sizeof(float)) , yMaxVal ); 208 EEPROM.put(0 + (12 * sizeof(float)) , iTrackMode ); 209 EEPROM.put(0 + (13 * sizeof(float)) , latitude ); 210 EEPROM.put(0 + (14 * sizeof(float)) , longitude ); 211 EEPROM.put(0 + (15 * sizeof(float)) , timezone ); 212 EEPROM.put(0 + (16 * sizeof(float)) , xMul ); 213 EEPROM.put(0 + (17 * sizeof(float)) , yMul ); 214 EEPROM.put(0 + (18 * sizeof(float)) , zMul ); 215 EEPROM.put(0 + (19 * sizeof(float)) , iXYS ); 216 EEPROM.put(0 + (20 * sizeof(float)) , iNightShutdown ); 217 EEPROM.put(0 + (30 * sizeof(float)) , trackername); 218 } 219} 220 221void setup() { 222 int led ; 223 224 Wire.begin(); 225 lcd.begin(20, 4); 226 lcd.home(); 227 lcd.setBacklightPin(3, NEGATIVE); 228 lcd.noCursor(); 229 lcd.clear(); 230 231 MCUSR &= ~_BV(WDRF); 232 wdt_disable(); 233 234 compass.init(); 235 compass.enableDefault(); 236 compass.setTimeout(1000); 237 238 Serial.begin(115200); // program/debug port 239 Serial1.begin(9600); // GPS port 240// Serial2.begin(115200); // Modbus Port to esp 241 Serial3.begin(115200); // EPS8266 serial converter 242 243 if (gyro.init()) { 244 gyro.enableDefault(); 245 hasGyro = true ; 246 } 247 if (pressure.begin()){ 248 Serial.println("BMP180 init success"); 249 hasPres = true ; 250 } 251 pinMode(RELAY_XZ_DIR, OUTPUT); // Outputs for PWM motor control 252 pinMode(RELAY_XZ_PWM, OUTPUT); // 253 pinMode(RELAY_YZ_PWM, OUTPUT); // 254 pinMode(RELAY_YZ_DIR, OUTPUT); // 255 iPWM_YZ = 0 ; 256 iPWM_XZ = 0 ; 257 pinMode(13, OUTPUT); // 258 digitalWrite(13, HIGH ); 259 ActivateRelays(0); // call an all stop first 260 261 mb_slave.begin( 9600 ); // RS-232 to base of tower 262 tempus = millis() + 100; 263 264 pinMode(UNUSED09, OUTPUT); // unused so dont leave floating set as output 265 pinMode(UNUSED10, OUTPUT); // 266 pinMode(UNUSED11, OUTPUT); // 267 pinMode(WATCHDOG, OUTPUT); // 268 digitalWrite(WATCHDOG,HIGH); 269 270 compass.m_min = (LSM303::vector<int16_t>) {-3848, -1822, -1551 }; // calibration figures are empirical 271 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300}; 272 273 LoadParamsFromEEPROM(true); 274 275 DS3231_init(DS3231_INTCN); // look for a rtc 276 DS3231_get(&tc); 277 DS3231_get(&td); 278 rtc_status = DS3231_get_sreg(); 279 if ((tc.mon == 0 )&& (tc.mday==0)){ // no rtc to load off 280 setTime (0,0,0,21,9,2017) ; // midnight on the equinox (will deactivae motors till gets a valid time) ; 281 }else{ 282 setTime((int)tc.hour,(int)tc.min,(int)tc.sec,(int)tc.mday,(int)tc.mon,(int)tc.year ) ; // set the internal RTC 283 hasRTC = true ; 284 } 285 286 HT.begin(0x00); 287 for (led = 0; led < 127; led++) { 288 HT.clearLedNow(led); 289 } 290 291 Serial.println("EtherNet init"); 292 if (SD.begin(chipSelect) || true){ 293 hasSD = true; 294 Serial.println("SD OK"); 295 Ethernet.begin(mac,ip,MyDNS); 296// Ethernet.begin(mac,ip); 297 if (Udp.begin(localPort)==1){ 298 hasNet = true ; 299 } 300 server.begin(); 301 } 302 Serial.print("server is at "); 303 Serial.println(Ethernet.localIP()); 304 305 306// wdt_enable(WDTO_8S); 307} 308 309// Arduino doesnt have these to we define from a sandard libruary 310float arcsin(float x) { 311 return (atan(x / sqrt(-x * x + 1))); 312} 313float arccos(float x) { 314 return (atan(x / sqrt(-x * x + 1)) + (2 * atan(1))); 315} 316// fractional orbital rotation in radians 317float gama(struct ts *tm) { 318 return ((2 * PI / 365 ) * DayOfYear(tm->year , tm->mon , tm->mday , tm->hour , tm->min )); 319} 320// equation of rime 321float eqTime(float g) { 322 return (229.18 * ( 0.000075 + ( 0.001868 * cos(g)) - (0.032077 * sin(g)) - (0.014615 * cos (2 * g)) - (0.040849 * sin(2 * g)))); 323} 324// declination of sun in radians 325float Decl(float g) { 326 return ( 0.006918 - (0.399912 * cos(g)) + (0.070257 * sin(g)) - (0.006758 * cos(2 * g)) + ( 0.000907 * sin(2 * g)) - ( 0.002697 * cos(3 * g)) + (0.00148 * sin(3 * g)) ); 327} 328float TimeOffset(float longitude , struct ts *tm , int timezone ) { 329 float dTmp ; 330 dTmp = (-4.0 * longitude ) + (60 * timezone) - eqTime(gama(tm)) ; 331 return (dTmp); 332} 333 334float TrueSolarTime(float longitude , struct ts *tm , int timezone ) { 335 float dTmp ; 336 dTmp = ( 60.0 * tm->hour ) + (1.0 * tm->min) + (1.0 * tm->sec / 60) - TimeOffset(longitude, tm, timezone) ; 337 return (dTmp); 338} 339float HourAngle(float longitude , struct ts *tm , int timezone) { 340 float dTmp; 341 dTmp = (TrueSolarTime(longitude, tm, timezone) / 4 ) - 180 ; // 720 minutes is solar noon -- div 4 is 180 342 return (dTmp); 343} 344// Hour angle for sunrise and sunset only 345float HA (float lat , struct ts *tm ) { 346 float latRad ; 347 latRad = lat * 2 * PI / 360 ; 348 return ( acos((cos(90.833 * PI / 180 ) / ( cos(latRad) * cos(Decl(gama(tm)))) - (tan(latRad) * tan(Decl(gama(tm)))))) / PI * 180 ); 349} 350 351float Sunrise(float longitude , float lat , struct ts *tm , int timezone) { 352 return (720 - ( 4.0 * (longitude + HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 353} 354float Sunset(float longitude , float lat , struct ts *tm , int timezone) { 355 return (720 - ( 4.0 * (longitude - HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 356} 357float SNoon(float longitude , float lat , struct ts *tm , int timezone) { 358 return (720 - ( 4.0 * (longitude + (60 * timezone) - eqTime(gama(tm)))) ) ; 359} 360 361float SolarZenithRad(float longitude , float lat , struct ts *tm , int timezone) { 362 float latRad ; 363 float decRad ; 364 float HourAngleRad ; 365 float dTmp ; 366 367 latRad = lat * 2 * PI / 360 ; 368 decRad = Decl(gama(tm)); 369 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 370 dTmp = acos((sin(latRad) * sin(decRad)) + (cos(latRad) * cos(decRad) * cos(HourAngleRad))); 371 return (dTmp) ; 372 373} 374float SolarElevationRad(float longitude , float lat , struct ts *tm , int timezone ) { 375 return ((PI / 2) - SolarZenithRad(longitude , lat , tm , timezone )) ; 376} 377 378float SolarAzimouthRad(float longitude , float lat , struct ts *tm , int timezone) { 379 float latRad ; 380 float decRad ; 381 float solarzenRad ; 382 float HourAngleRad ; 383 float dTmp ; 384 latRad = lat * 2 * PI / 360 ; 385 decRad = Decl(gama(tm)); 386 solarzenRad = SolarZenithRad ( longitude , lat , tm , timezone ) ; 387 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 388 dTmp = acos(((sin(decRad) * cos(latRad)) - (cos(HourAngleRad) * cos(decRad) * sin(latRad))) / sin(solarzenRad)) ; 389 if ( HourAngleRad < 0 ) { 390 return (dTmp) ; 391 } else { 392 return ((2 * PI) - dTmp) ; 393 } 394} 395 396 397void StopYZ(){ 398 iPWM_YZ=0 ; 399 motor_recycle = MOTOR_DWELL ; 400} 401void StopXZ(){ 402 iPWM_XZ=0 ; 403 motor_recycle = MOTOR_DWELL ; 404} 405 406void ActivateRelays(int iAllStop) { 407 if (motor_recycle > 0 ){ 408 motor_recycle-- ; 409 } 410 if ( iAllStop == 0 ) { 411 StopYZ() ; 412 StopXZ() ; 413 } else { 414 if (( iPWM_YZ==0 ) && (motor_recycle == 0 )){ 415 if (((yzAng ) < ( yzTarget - yzH )) ) { // do Y ie E/W before N/S 416 digitalWrite(RELAY_YZ_DIR, LOW) ; 417 iPWM_YZ=2 ; 418 } 419 if (((yzAng ) > (yzTarget + yzH )) ) { 420 digitalWrite(RELAY_YZ_DIR, HIGH) ; 421 iPWM_YZ=2 ; 422 } 423 } 424 if ( iPWM_YZ>0 ){ 425 if ((yzAng > yzTarget) && ( digitalRead(RELAY_YZ_DIR)==LOW )) { 426 StopYZ() ; 427 } 428 if ((yzAng < yzTarget) && ( digitalRead(RELAY_YZ_DIR)==HIGH )) { 429 StopYZ() ; 430 } 431 } 432 433 if (( iPWM_YZ==0)) { // if finished on E/W you can do N/S 434 if (( iPWM_XZ==0 ) && (motor_recycle == 0 )){ 435 if ((xzAng < ( xzTarget - xzH )) ) { // turn on if not in tolerance 436 digitalWrite(RELAY_XZ_DIR, LOW) ; 437 iPWM_XZ=2 ; 438 } 439 if ((xzAng > ( xzTarget + xzH )) ) { // turn on if not in tolerance 440 digitalWrite(RELAY_XZ_DIR, HIGH) ; 441 iPWM_XZ=2 ; 442 } 443 } 444 }else{ 445 if ((iPWM_XZ>0 )){ 446 StopXZ() ; 447 } 448 } 449 if ( iPWM_XZ>0 ){ 450 if ((xzAng > xzTarget ) && ( digitalRead(RELAY_XZ_DIR)==LOW )) { // if on turn off 451 StopXZ() ; 452 } 453 if ((xzAng < xzTarget ) && ( digitalRead(RELAY_XZ_DIR)==HIGH )) { // if on turn off 454 StopXZ() ; 455 } 456 } 457 } 458 if (iPWM_XZ>0){ 459 iPWM_XZ += 3 ; 460 } 461 if (iPWM_YZ>0){ 462 iPWM_YZ += 3 ; 463 } 464 iPWM_XZ = constrain(iPWM_XZ,0,254); 465 iPWM_YZ = constrain(iPWM_YZ,0,254); 466 analogWrite(RELAY_XZ_PWM,iPWM_XZ); 467 analogWrite(RELAY_YZ_PWM,iPWM_YZ); 468} 469 470void FloatToModbusWords(float src_value , uint16_t * dest_lo , uint16_t * dest_hi ) { 471 uint16_t tempdata[2] ; 472 float *tf ; 473 tf = (float * )&tempdata[0] ; 474 *tf = src_value ; 475 *dest_lo = tempdata[1] ; 476 *dest_hi = tempdata[0] ; 477} 478float FloatFromModbusWords( uint16_t dest_lo , uint16_t dest_hi ) { 479 uint16_t tempdata[2] ; 480 float *tf ; 481 tf = (float * )&tempdata[0] ; 482 tempdata[1] = dest_lo ; 483 tempdata[0] = dest_hi ; 484 return (*tf) ; 485} 486 487 488float LoadFloatFromEEPROM(int address,float minval,float maxval, float defaultval){ 489float tmp ; 490 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 491 if (( tmp < minval ) || ( tmp > maxval )|| (NumberOK(tmp) == 1)) { 492 tmp = defaultval ; 493 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 494 } 495 return(tmp); 496} 497int LoadIntFromEEPROM(int address,int minval,int maxval, int defaultval){ 498int tmp ; 499 EEPROM.get(0 + (address * sizeof(float)) , tmp ); // float.. yeah yeah I know... but it makes it compatible with the one above (easy on the brain) 500 if (( tmp < minval ) || ( tmp > maxval )) { 501 tmp = defaultval ; 502 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 503 } 504 return(tmp); 505} 506 507int NumberOK (float target) { 508 int tmp = 0 ; 509 tmp = isnan(target); 510 if ( tmp != 1 ) { 511 tmp = isinf(target); 512 } 513 return (tmp); 514} 515 516int SetTimeFromGPS(){ 517 byte hundredths ; 518 519 gps.crack_datetime((int *)&tg.year,(byte *)&tg.mon,(byte *) &tg.mday,(byte *) &tg.hour,(byte *) &tg.min,(byte *) &tg.sec , &hundredths, &fixage); 520 setTime((int)tg.hour,(int)tg.min,(int)tg.sec,(int)tg.mday,(int)tg.mon,(int)tg.year ) ; // set the internal RTC from last GPS time 521 chiptime = now() ; // get it back again 522 chiptime += (( timezone * SECS_PER_HOUR ) + ( fixage / 1000 )) ; // add the offset plus the fix age 523 setTime(chiptime); // set it again 524 if (hasRTC) { 525 tg.year = year(); 526 tg.mon = month() ; 527 tg.mday = day(); 528 tg.hour = hour() ; 529 tg.min = minute(); 530 tg.sec = second(); 531 DS3231_set(tg); //should also update this 532 } 533 return(0); 534} 535 536 537void loop() { 538 float P; 539 float sunInc; 540 float sunAng; 541 float xzRatio; 542 float yzRatio; 543 float decl ; 544 float eqtime ; 545 float dTmp ; 546 float heading ; 547 float tst ; 548 float flat, flon; 549 unsigned short goodsent; 550 unsigned short failcs; 551// int iYear , iMon , iMday , iHour , iMin , iSec ; 552 553 554 if (minute() != rtc_min) { // do onlyonce a minute 555 gps.stats(&gpschars, &goodsent , &failcs ); 556 gps.f_get_position(&flat, &flon,(long unsigned *) &fixage); // return in degrees 557 if (hasPres){ 558 Pr = getPressure((double *)&gT) ; 559 } 560 if (hasRTC) { 561 T = DS3231_get_treg(); 562 } 563 rtc_min = minute() ; 564 565 if ((fixage > 0 ) && ( fixage < 40000 )) { // wait till our fix is valid before we use the values 566 latitude = flat ; 567 longitude = flon ; 568 iGPSLock = gps.satellites() ; 569 alt = gps.f_altitude() ; 570 if (iPowerUp==0) { // only do this at startup so we have a better position ref for next time 571 EEPROM.put(0 + (13 * sizeof(float)) , latitude ); 572 EEPROM.put(0 + (14 * sizeof(float)) , longitude ); 573 iPowerUp = 1 ; 574 if (!hasNet ){ 575 SetTimeFromGPS(); 576 } 577 } 578 }else{ 579 iGPSLock = 0 ; // if no lock loook at internal clock 580 } 581 } 582 tc.year = year(); 583 tc.mon = month() ; 584 tc.mday = day(); 585 tc.hour = hour() ; 586 tc.min = minute(); 587 tc.sec = second(); 588 589 compass.read(); // this reads all 6 channels 590 if ( hasGyro ){ 591 gyro.read(); 592 xRoll = gyro.g.x ; 593 yRoll = gyro.g.y ; 594 zRoll = gyro.g.z ; 595 } 596 597 digitalWrite(UNUSED09,!digitalRead(UNUSED09)); // toggle this output so I can measure the cycle time with a scope 598 599 heading = compass.heading((LSM303::vector<int>) { 1, 0, 0 }); 600 601 if (( compass.a.z != 0) && (!compass.timeoutOccurred() )) { 602 zAng = (float)compass.a.z ; 603 if (iXYS == 0 ){ // Proper Job make it configurable 604 xzRatio = (float)compass.a.x * xMul / abs(zAng) ; // Normal 605 yzRatio = (float)compass.a.y * yMul / abs(zAng) ; 606 }else{ 607 xzRatio = (float)compass.a.y * xMul / abs(zAng) ; // Swapped 608 yzRatio = (float)compass.a.x * yMul / abs(zAng) ; 609 } 610 xzAng = ((float)atan(xzRatio) / PI * 180 ) + xzOffset ; // good old offsets or fudge factors 611 yzAng = ((float)atan(yzRatio) / PI * 180 ) + yzOffset ; 612// digitalWrite(13, LOW); 613 }else{ // try restarting the compass/accelerometer modual - cos he gone walkabout... 614 Wire.begin(); // reset the I2C 615 compass.init(); 616 compass.enableDefault(); 617 compass.setTimeout(1000); // BTW I fixed up the int / long issue in the time out function in the LM303 lib I was using 618 compass.m_min = (LSM303::vector<int16_t>) {-3848, -1822, -1551 }; // calibration figures are empirical (just whirl it around a bit and records the min max !!) 619 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300 }; 620/* if (tc.sec % 2 == 0 ) { 621 digitalWrite(13, HIGH); 622 } else { 623 digitalWrite(13, LOW); 624 }*/ 625 HT.begin(0x00); 626 } 627 628 if (setchiptime > 0) { // update the arduino time from the modbus register then clear it... also set RTC if fitted 629 setTime(setchiptime) ; 630 if (hasRTC) { 631 tc.year = year(); 632 tc.mon = month() ; 633 tc.mday = day(); 634 tc.hour = hour() ; 635 tc.min = minute(); 636 tc.sec = second(); 637 DS3231_set(tc); //should also update this 638 } 639 setchiptime = 0 ; 640 } 641 642 if ( hour() != rtc_hour){ // update our time every hour if we can 643 if (iGPSLock == 0){ 644 if( hasNet ){ 645 sendNTPpacket(timeServer); // send an NTP packet to a time server every hour 646 }else{ 647 if (hasRTC) { 648 DS3231_get(&td); 649 setTime((int)td.hour,(int)td.min,(int)td.sec,(int)td.mday,(int)td.mon,(int)td.year ) ; // set the internal RTC from Dallas RTC 650 } 651 } 652 }else{ 653 if ((fixage > 0 ) && ( fixage < 10000 )) { // if the lock is less than 10 second old 654 SetTimeFromGPS(); 655 } 656 } 657 rtc_hour = hour() ; 658 } 659 660 if ( rtc_sec != second() ) { //only update once a second 661 wdt_reset(); // reset internal watchdog - good puppy 662 663 if (( tc.sec > 8 ) && ( tc.sec < 58 )) { // dont calculate arround the minute when time is updating from NTP or GPS as might get a not so funny result 664 digitalWrite(13,!digitalRead(13)); 665 solar_az_deg = SolarAzimouthRad(longitude, latitude, &tc, timezone) * 180 / PI ; 666 solar_el_deg = SolarElevationRad(longitude, latitude, &tc, timezone) * 180 / PI ; 667 668 decl = Decl(gama(&tc)) * 180 / PI ; 669 ha = HourAngle (longitude , &tc , timezone ) ; 670 sunrise = Sunrise(longitude, latitude, &tc, timezone) ; 671 sunset = Sunset(longitude, latitude, &tc, timezone); 672 tst = TrueSolarTime(longitude, &tc, timezone); 673 sunX = abs(latitude) + decl ; 674 if (solar_el_deg >= 0 ){ // day 675 iDayNight = 1 ; 676 }else{ // night 677 iDayNight = 0 ; 678 } 679 } 680 switch (iTrackMode) { 681 case 4: // both axis to park 682 yzTarget = dyPark ; // night park position E/W 683 xzTarget = dxPark ; // night park position N/S 684 break ; 685 case 3: // both axis off no tracking 686 break ; 687 case 2: // xz tracking NS 688 if ( iDayNight == 1 ) { 689 xzTarget = sunX ; // need to map the coordinate system correctly 690 } else { 691 xzTarget = dxPark ; // night park position 692 } 693 break; 694 case 1: // yz tracking EW 695 if (iDayNight == 1) { 696 yzTarget = ha ; 697 } else { 698 yzTarget = dyPark ; // night park position 699 } 700 break; 701 case -1: // set target to tracking and park both at nigh 702 if (iDayNight == 1) { 703 yzTarget = ha ; 704 xzTarget = sunX ; // need to map the coordinate system correctly 705 } else { 706 yzTarget = dyPark ; // night park position E/W 707 xzTarget = dxPark ; // night park position N/S 708 } 709 break; 710 default: // set target to tracking 711 if (iDayNight == 1) { 712 yzTarget = ha ; 713 xzTarget = sunX ; // need to map the coordinate system correctly 714 } else { 715 yzTarget = dyPark ; // night park position (dont park the other - leave till morning) 716 } 717 break; 718 } 719 xzTarget = constrain(xzTarget,xMinVal,xMaxVal); // constain function... very cool - dont leave home without it ! 720 yzTarget = constrain(yzTarget,yMinVal,yMaxVal); 721 722 lcd.setCursor ( 0, 0 ); // Diags in case there is an LCD display attached 723 lcd.print("X/Z "); 724 if ( xzAng > 0 ) { 725 lcd.print("+"); 726 } 727 lcd.print(xzAng); 728 lcd.setCursor ( 10, 0 ); 729 lcd.print("Y/Z "); 730 if ( yzAng > 0 ) { 731 lcd.print("+"); 732 } 733 lcd.print(yzAng); 734 lcd.setCursor ( 0, 1 ); 735 lcd.print("TX "); 736 if (( xzTarget) > 0 ) { 737 lcd.print("+"); 738 } 739 lcd.print(( xzTarget)); 740 lcd.setCursor ( 10, 1 ); 741 lcd.print("TY "); 742 if (( yzTarget) > 0 ) { 743 lcd.print("+"); 744 } 745 lcd.print(( yzTarget)); 746 lcd.setCursor ( 0, 2 ); 747 lcd.print("DX "); 748 dTmp = ( xzAng - xzTarget) ; 749 if (dTmp > 0 ) { 750 lcd.print("+"); 751 } 752 lcd.print(dTmp); 753 lcd.setCursor ( 10, 2 ); 754 lcd.print("DY "); 755 dTmp = ( yzAng - yzTarget) ; 756 if (dTmp > 0 ) { 757 lcd.print("+"); 758 } 759 lcd.print(dTmp); 760 761 lcd.setCursor ( 0, 3 ); // line 3 762 snprintf(buff, BUFF_MAX, "%02d:%02d:%02d", tc.hour, tc.min, tc.sec); 763 lcd.print(buff) ; 764 765 lcd.setCursor ( 9, 3 ); // line 3 766 lcd.print( "S") ; 767 if ( iDayNight == 0 ) { 768 lcd.print( "-") ; 769 }else{ 770 if (( tc.sec % 2 ) == 0 ) { 771 lcd.print( "<") ; 772 }else{ 773 lcd.print( ">") ; 774 } 775 } 776 if ( gps.satellites() > 9 ){ 777 lcd.print( "-" ); 778 }else{ 779 lcd.print(gps.satellites()) ; 780 } 781 782 lcd.setCursor ( 13, 3 ); // line 3 783 snprintf(buff, BUFF_MAX, "%04X", fixage); 784 lcd.print(buff) ; 785 786 lcd.setCursor ( 18, 3 ); // line 3 787 if ( iPWM_YZ == 0 ) { 788 lcd.print( " ") ; 789 }else{ 790 if (( digitalRead(RELAY_YZ_DIR) == LOW )) { 791 lcd.print( "W") ; 792 }else{ 793 lcd.print( "E") ; 794 } 795 } 796 lcd.setCursor ( 19, 3 ); // line 3 797 if ( iPWM_XZ == 0 ) { 798 lcd.print( " ") ; 799 }else{ 800 if (( digitalRead(RELAY_XZ_DIR) == LOW )) { 801 lcd.print( "N") ; 802 }else{ 803 lcd.print( "S") ; 804 } 805 } 806 807 rtc_sec = second() ; 808 DisplayMeatBall() ; 809 } 810 811 if ( iDoSave == 2 ) { // save them Active via web or 812 LoadParamsFromEEPROM(false); 813 iDoSave = 0 ; // only do once 814 } 815 if ( iDoSave == 3 ) { // load them 816 LoadParamsFromEEPROM(true); 817 iDoSave = 0 ; // only do once 818 } 819 820 if (((tc.hour > 19 ) || ( tc.hour < 5 )) && (iTrackMode < 3)) { 821 if ( iNightShutdown != 0 ){ 822 ActivateRelays(1) ; 823 }else{ 824 ActivateRelays(0) ; // power down at night if in tracking mode 825 } 826 }else{ 827 ActivateRelays(1) ; 828 } 829 830 831 state1 = mb_slave.poll( (uint16_t*)&iMode, MAX_MODBUS_DATA ); 832 833 switch (state1) { 834 case EXC_ADDR_RANGE: 835 Serial.println("EXC_ADDR_RANGE PORT 2"); 836 break; 837 case EXC_FUNC_CODE: 838 Serial.println("EXC_FUNC_CODE PORT 2"); 839 break; 840 case EXC_REGS_QUANT: 841 Serial.println("EXC_REGS_QUANT PORT 2"); 842 break; 843 } 844 845 if ( hasNet ) { 846 if ( Udp.parsePacket() ) { // check if NTP packet arrived back 847 processNTPpacket(); 848 } 849 850 client = server.available(); // process web server 851 if (client) { 852 processTCPClient(client); 853 } 854 } 855 856 while (Serial1.available()){ // process the gps buffer 857 gps.encode(Serial1.read()); 858 } 859} // end of loop 860 861 862 863 864 865 866float DayOfYear(uint16_t iYear , uint8_t iMon , uint8_t iDay , uint8_t iHour , uint8_t iMin ) { 867 int i ; 868 float iTDay ; 869 870 iTDay = iDay - 1 ; // this is zero referenced 871 for ( i = 1 ; i < iMon ; i++ ) { 872 switch (i) { 873 case 1: 874 case 3: 875 case 5: 876 case 7: 877 case 8: 878 case 10: 879 case 12: 880 iTDay += 31 ; 881 break; 882 case 4: 883 case 6: 884 case 9: 885 case 11: 886 iTDay += 30 ; 887 break; 888 case 2 : 889 if ((iYear % 4) == 0 ) { 890 iTDay += 29 ; 891 } else { 892 iTDay += 28 ; 893 } 894 break; 895 } 896 } 897 iTDay += (( 1.0 * iHour - 12 ) / 24 ) ; 898 // iDay += 1.0 * iMin / 1440 ; 899 return (iTDay); 900} 901 902 903int HrsSolarTime(float target) { 904 int i ; 905 i = target ; 906 return ( i / 60 ); 907} 908int MinSolarTime(float target) { 909 int i ; 910 i = target ; 911 return ( i % 60 ); 912} 913 914float sign(float target) { 915 if (target > 0 ) { 916 return (1); 917 } else { 918 if (target < 0 ) { 919 return (-1); 920 } else { 921 return (0); 922 } 923 } 924} 925 926void DisplayMeatBall() { 927 int pos , led , x , y; 928 float dx , dy ; 929 float dxa , dya ; 930 931 HT.setBrightness(15); 932 933 dx = xzAng - xzTarget ; 934 dy = yzAng - yzTarget ; 935 dxa = abs(dx) ; 936 dya = abs(dy) ; 937 if (dxa < 6) { 938 x = 0 ; 939 } else { 940 if (dxa < 12) { 941 x = sign(dx); 942 } else { 943 if (dxa < 50) { 944 x = 2 * sign(dx); 945 } else { 946 x = 3 * sign(dx); 947 } 948 } 949 } 950 if (dya < 6) { 951 y = 0 ; 952 } else { 953 if (dya < 12) { 954 y = sign(dy); 955 } else { 956 if (dya < 25) { 957 y = 2 * sign(dy); 958 } else { 959 y = 3 * sign(dy); 960 } 961 } 962 } 963 pos = 27 ; // netral position 964 pos += (y * 8) ; // add or sumtract the x in range of -3 to +3 965 pos += (x ) ; // add or sumtract 8 * y or y in range of -3 to +3 966 for (led = 0; led < 63; led++) { 967 switch (led){ 968 case 0: 969 case 7: 970 case 56: 971 case 63: 972 break; 973 default: 974 HT.clearLedNow(MapLedNo(led)); 975 break; 976 } 977 } 978 979 if ( ++iPMode > 100 ) { 980 iPMode = 1 ; 981 } 982 switch(gps.satellites()){ 983 case 255: 984 HT.clearLedNow(MapLedNo(0)); // turn off four courners 985 HT.clearLedNow(MapLedNo(7)); // turn off four courners 986 HT.clearLedNow(MapLedNo(63)); // turn off four courners 987 HT.clearLedNow(MapLedNo(56)); // turn off four courners 988 break; 989 case 1: 990 case 2: 991 HT.setLedNow(MapLedNo(0)); // turn on four courners 992 HT.clearLedNow(MapLedNo(7)); // turn on four courners 993 HT.clearLedNow(MapLedNo(63)); // turn on four courners 994 HT.clearLedNow(MapLedNo(56)); // turn on four courners 995 break; 996 case 3: 997 HT.setLedNow(MapLedNo(0)); // turn on four courners 998 HT.setLedNow(MapLedNo(7)); 999 HT.clearLedNow(MapLedNo(63)); // turn on four courners 1000 HT.clearLedNow(MapLedNo(56)); // turn on four courners 1001 break; 1002 case 4: 1003 HT.setLedNow(MapLedNo(0)); // turn on four courners 1004 HT.setLedNow(MapLedNo(7)); 1005 HT.setLedNow(MapLedNo(56)); 1006 HT.clearLedNow(MapLedNo(63)); // turn on four courners 1007 break; 1008 default: 1009 HT.setLedNow(MapLedNo(0)); // turn on four courners 1010 HT.setLedNow(MapLedNo(7)); 1011 HT.setLedNow(MapLedNo(56)); 1012 HT.setLedNow(MapLedNo(63)); 1013 break; 1014 } 1015 1016 1017 // HT.setLedNow(MapLedNo(tc.sec)); 1018 if ((iPWM_YZ == 0) && (iPWM_XZ == 0)) { 1019// HT.setBlinkRate(HT16K33_DSP_NOBLINK); // not attempting to move 1020 if ((tc.sec % 2) == 0 ) { 1021 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1022 HT.setLedNow(MapLedNo(pos + 9)); 1023 }else{ 1024 HT.setLedNow(MapLedNo(pos + 1)); 1025 HT.setLedNow(MapLedNo(pos + 8)); 1026 } 1027 } else { 1028// HT.setBlinkRate(HT16K33_DSP_BLINK2HZ); //moving so blink meat ball 1029 if (( iPMode % 2 ) == 0 ){ 1030 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1031 HT.setLedNow(MapLedNo(pos + 1)); 1032 HT.setLedNow(MapLedNo(pos + 8)); 1033 HT.setLedNow(MapLedNo(pos + 9)); 1034 } 1035 } 1036} 1037 1038 1039int MapLedNo(int target) // this compensates for the screwy setup of the matrix driver to the chip 1040{ 1041 int row ; 1042 int col ; 1043 row = target / 8 ; 1044 col = target % 8 ; 1045 if (col == 0 ) { 1046 return ((row * 16 ) + 7) ; 1047 } else { 1048 return ((row * 16 ) + col - 1) ; 1049 } 1050} 1051 1052 1053unsigned long sendNTPpacket(char* address) // send an NTP request to the time server at the given address 1054{ 1055 memset(packetBuffer, 0, NTP_PACKET_SIZE); // set all bytes in the buffer to 0 1056 // Initialize values needed to form NTP request 1057 // (see URL above for details on the packets) 1058 packetBuffer[0] = 0b11100011; // LI, Version, Mode 1059 packetBuffer[1] = 0; // Stratum, or type of clock 1060 packetBuffer[2] = 6; // Polling Interval 1061 packetBuffer[3] = 0xEC; // Peer Clock Precision 1062 // 8 bytes of zero for Root Delay & Root Dispersion 1063 packetBuffer[12] = 49; 1064 packetBuffer[13] = 0x4E; 1065 packetBuffer[14] = 49; 1066 packetBuffer[15] = 52; 1067 1068 // all NTP fields have been given values, now 1069 // you can send a packet requesting a timestamp: 1070 Udp.beginPacket(address, 123); //NTP requests are to port 123 1071 Udp.write(packetBuffer, NTP_PACKET_SIZE); 1072 Udp.endPacket(); 1073} 1074 1075 1076unsigned long processNTPpacket(void){ 1077 struct ts tntp; 1078 Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer 1079 1080 //the timestamp starts at byte 40 of the received packet and is four bytes, 1081 // or two words, long. First, esxtract the two words: 1082 1083 unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); 1084 unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 1085 // combine the four bytes (two words) into a long integer 1086 // this is NTP time (seconds since Jan 1 1900): 1087 unsigned long secsSince1900 = highWord << 16 | lowWord; 1088 Serial.print(F("Seconds since Jan 1 1900 = " )); 1089 Serial.println(secsSince1900); 1090 1091 // now convert NTP time into everyday time: 1092 Serial.print(F("Unix time = ")); 1093 // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: 1094 const unsigned long seventyYears = 2208988800UL; 1095 // subtract seventy years: 1096 unsigned long epoch = secsSince1900 - seventyYears + (timezone * SECS_PER_HOUR); 1097 // print Unix time: 1098 Serial.println(epoch); 1099 setTime((time_t)epoch); // update the clock 1100 t.year = year(); // record the last NTP time set 1101 t.mon = month() ; 1102 t.mday = day(); 1103 t.hour = hour(); 1104 t.min = minute(); 1105 t.sec = second(); 1106 1107 // print the hour, minute and second: 1108 Serial.print(F("In time zone ")); 1109 Serial.print(timezone); 1110 Serial.print(F(" the time is ")); 1111 Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) 1112 Serial.print(':'); 1113 if ( ((epoch % 3600) / 60) < 10 ) { 1114 // In the first 10 minutes of each hour, we'll want a leading '0' 1115 Serial.print('0'); 1116 } 1117 Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) 1118 Serial.print(':'); 1119 if ( (epoch % 60) < 10 ) { 1120 // In the first 10 seconds of each minute, we'll want a leading '0' 1121 Serial.print('0'); 1122 } 1123 Serial.println(epoch % 60); // print the second 1124} 1125 1126void sendTDTREnd(){ 1127 client.println(F("</td></tr>")); 1128} 1129 1130void processTCPClient(EthernetClient Target){ 1131 boolean currentLineIsBlank = true; 1132 long j , i = 0 ; 1133 while (Target.connected()) { 1134 if (Target.available()) { 1135 String req = Target.readStringUntil('\r'); 1136 // so you can send a reply 1137 // send a standard http response header 1138 i = req.indexOf("?tname="); // wanta do this one BEFORE we dick with captialisation of the request 1139 if (i != -1){ // have a request to set the time zone 1140 j = req.indexOf("&"); 1141 req.substring(i+7,j).toCharArray(trackername , 16) ; 1142 for (j=0 ; j < 16 ; j++){ 1143 if (trackername[j] == '+' ) { 1144 trackername[j] = ' ' ; 1145 } 1146 } 1147 } 1148 1149 i = req.indexOf("/gpio/"); 1150// Serial.println(req); 1151// Serial.println(req.charAt(i+6)); 1152 if (i != -1){ 1153 switch(req.charAt(i+6)){ 1154 case '0': 1155 break; 1156 case '1': 1157 break; 1158 case '2': 1159 break; 1160 case '3': 1161 break; 1162 case '4': 1163 break; 1164 case '5': 1165 break; 1166 case '6': 1167 break; 1168 case '7': 1169 break; 1170 case 'A': 1171 break; 1172 case 'B': 1173 break; 1174 case 'C': 1175 break; 1176 case 'T': // TssmmhhWDDMMYYYY aka set time same format as example eg T001513624032017 1177 t.sec = req.substring(i+7,i+9).toInt(); // 1 1178 t.min = req.substring(i+9,i+11).toInt(); // 3 1179 t.hour = req.substring(i+11,i+13).toInt(); // 5 1180 t.wday = req.charAt(i+13) - 48; // 7 1181 t.mday = req.substring(i+14,i+16).toInt(); // 8 1182 t.mon = req.substring(i+16,i+18).toInt();; // 10 1183 t.year = req.substring(i+18).toInt(); // 12 1184// snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 1185// Serial.println(buff) ; 1186 DS3231_set(t); // set external RTC 1187 setTime((int)t.hour,(int)t.min,(int)t.sec,(int)t.mday,(int)t.mon,(int)t.year ) ; // set the internal RTC 1188 1189 break; 1190 case 't': // force NTP packet 1191 sendNTPpacket(timeServer); // send an NTP packet if requested to 1192 break; 1193 case 'S': // save all the params 1194 iDoSave = 2 ; 1195 break; 1196 case 'R': // read params back from eeprom 1197 iDoSave = 3 ; 1198 break; 1199 case 'Y': 1200 break; 1201 case 'X': 1202 break; 1203 default: 1204 break; 1205 } 1206 }else{ 1207 req.toLowerCase(); 1208 i = req.indexOf(".csv"); 1209 if (i != -1){ // have a request get a CSV filefrom the SD card 1210 i = req.indexOf(".csv"); 1211 Target.flush(); 1212 Target.println(F("HTTP/1.1 200 OK")); 1213 Target.println(F("Content-Type: text/csv")); 1214 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1215 Target.println(""); // this blank line be important 1216 SendFile(req.substring(4,i+4),Target); 1217 break; 1218 }else{ 1219 i = req.indexOf(".jpg"); 1220 if (i != -1){ // have a request get a CSV filefrom the SD card 1221 i = req.indexOf(".jpg"); 1222 Serial.println(req); 1223 Serial.println(req.substring(4,i+4)); 1224 Target.flush(); 1225 Target.println(F("HTTP/1.1 200 OK")); 1226 Target.println(F("Content-Type: image/jpeg")); 1227 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1228 Target.println(""); // this blank line be important 1229 SendFile(req.substring(4,i+4),Target); 1230 break; 1231 }else{ 1232 i = req.indexOf("index.htm"); 1233 if (i != -1){ // have a request get a CSV filefrom the SD card 1234 Target.flush(); 1235 Target.println(F("HTTP/1.1 200 OK")); 1236 Target.println(F("Content-Type: text/html")); 1237 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1238 Target.println(""); // this blank line be important 1239 Target.println(F("<!DOCTYPE HTML>")); 1240 Target.println(F("<head><title>Team Trouble SD Card</title>")); 1241 Target.println(F("<meta name=viewport content='width=320, auto inital-scale=1'>")); 1242 Target.print(F("</head><body><html><center><b><h2>Team Trouble SD Card</h2></b><br><pre>")); 1243 File root = SD.open("/"); 1244 printDirectory(root,0,Target,"/"); 1245 root.close(); 1246 Target.println("</pre><br><br><a href=''>Home Page</a></body></html>"); 1247 break; 1248 }else{ 1249 i = req.indexOf("stats.htm"); 1250 if (i != -1){ // have a request get a status.htm 1251 Target.flush(); 1252 Target.println(F("HTTP/1.1 200 OK")); 1253 Target.println(F("Content-Type: text/html")); 1254 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1255 Target.println(""); // this blank line be important 1256 Target.println(F("<!DOCTYPE HTML>")); 1257 Target.println(F("<head><title>Stats Page</title>")); 1258 Target.println(F("<meta name=viewport content='width=320, auto inital-scale=1'>")); 1259 Target.print(F("</head><body><html><center><b><h2>Stats Page</h2></b>")); 1260 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 1261 Target.println(buff) ; 1262 Target.println(F("</table><br><br><a href=''>Home Page</a></body></html>")); 1263 break; 1264 } 1265 } 1266 } 1267 } 1268 } 1269 1270 i = req.indexOf("?tmode="); 1271 if (i != -1){ // have a request to set the tracking mode 1272 iTrackMode = req.substring(i+7).toInt() ; 1273 if (( iTrackMode < -1) || ( iTrackMode > 4 )){ 1274 iTrackMode = -1 ; 1275 } 1276 } 1277 i = req.indexOf("?tzone="); 1278 if (i != -1){ // have a request to set the time zone 1279 timezone = req.substring(i+7).toInt() ; 1280 if (( timezone < -23) || ( timezone > 23 )){ 1281 timezone = 10 ; 1282 } 1283 } 1284 i = req.indexOf("?mylat="); //lat 1285 if (i != -1){ // have a request to set the latitude 1286 latitude = req.substring(i+7).toFloat() ; 1287 if (( latitude < -90) || ( latitude > 90 )){ 1288 latitude = -34.051219 ; 1289 } 1290 } 1291 i = req.indexOf("?mylon="); // long 1292 if (i != -1){ // have a request to set the logitude 1293 longitude = req.substring(i+7).toFloat() ; 1294 if (( longitude < -180) || ( longitude > 180 )){ 1295 longitude = 142.013618 ; 1296 } 1297 } 1298 i = req.indexOf("?minay="); 1299 if (i != -1){ // have a request to set minimum angle Y 1300 yMinVal = req.substring(i+7).toFloat() ; 1301 if (( yMinVal < -70) || ( yMinVal > 50 )){ 1302 yMinVal = -65 ; // set to default 1303 } 1304 } 1305 i = req.indexOf("?minax="); 1306 if (i != -1){ // have a request to set minimum angle X 1307 xMinVal = req.substring(i+7).toFloat() ; 1308 if (( xMinVal < -10) || ( xMinVal > 60 )){ 1309 xMinVal = 0 ; // set to default 1310 } 1311 } 1312 i = req.indexOf("?maxay="); 1313 if (i != -1){ // have a request to set maximum angle Y 1314 yMaxVal = req.substring(i+7).toFloat() ; 1315 if (( yMaxVal < -70) || ( yMaxVal > 50 )){ 1316 yMaxVal = 45 ; // set to default 1317 } 1318 } 1319 i = req.indexOf("?maxax="); 1320 if (i != -1){ // have a request to set maximum angle X 1321 xMaxVal = req.substring(i+7).toFloat() ; 1322 if (( xMaxVal < -10) || ( xMaxVal > 60 )){ 1323 xMaxVal = 50 ; // set to default 1324 } 1325 } 1326 i = req.indexOf("?paray="); 1327 if (i != -1){ // have a request to set park angle Y 1328 dyPark = req.substring(i+7).toFloat() ; 1329 if (( dyPark < -70) || ( dyPark > 50 )){ 1330 dyPark = 0 ; // set to default 1331 } 1332 } 1333 i = req.indexOf("?parax="); 1334 if (i != -1){ // have a request to set park angle X 1335 dxPark = req.substring(i+7).toFloat() ; 1336 if (( dxPark < -10) || ( dxPark > 60 )){ 1337 dxPark = 6 ; // set to default 1338 } 1339 } 1340 i = req.indexOf("?offay="); 1341 if (i != -1){ // have a request to set offset angle Y 1342 yzOffset = req.substring(i+7).toFloat() ; 1343 if (( yzOffset < -20) || ( yzOffset > 20 )){ 1344 yzOffset = 0 ; // set to default 1345 } 1346 } 1347 i = req.indexOf("?offax="); 1348 if (i != -1){ // have a request to set offset angle X 1349 xzOffset = req.substring(i+7).toFloat() ; 1350 if (( xzOffset < -20) || ( xzOffset > 20 )){ 1351 xzOffset = 0 ; // set to default 1352 } 1353 } 1354 i = req.indexOf("?hysay="); 1355 if (i != -1){ // have a request to set Hysterisis angle 1356 yzH = req.substring(i+7).toFloat() ; 1357 if (( yzH < -20) || ( yzH > 20 )){ 1358 yzH = 4 ; // set to default 1359 } 1360 } 1361 i = req.indexOf("?hysax="); 1362 if (i != -1){ // have a request to set Hysterisis angle 1363 xzH = req.substring(i+7).toFloat() ; 1364 if (( xzH < -20) || ( xzH > 20 )){ 1365 xzH = 4 ; // set to default 1366 } 1367 } 1368 i = req.indexOf("?taray="); 1369 if (i != -1){ // have a request to set Hysterisis angle 1370 yzTarget = req.substring(i+7).toFloat() ; 1371 if (( yzTarget < -70) || ( yzTarget > 50 )){ 1372 yzTarget = 0 ; // set to default 1373 } 1374 } 1375 i = req.indexOf("?tarax="); 1376 if (i != -1){ // have a request to set Hysterisis angle 1377 xzTarget = req.substring(i+7).toFloat() ; 1378 if (( xzTarget < -10) || ( xzTarget > 60 )){ 1379 xzTarget = 0 ; // set to default 1380 } 1381 } 1382 1383 i = req.indexOf("?mulax="); 1384 if (i != -1){ // have a request to set Axis Multiplier 1385 xMul = req.substring(i+7).toFloat() ; 1386 if (( xMul < -10) || ( xMul > 10 )){ 1387 xMul = 1.0 ; // set to default 1388 } 1389 } 1390 i = req.indexOf("?mulay="); 1391 if (i != -1){ // have a request to set Axis Multiplier 1392 yMul = req.substring(i+7).toFloat() ; 1393 if (( yMul < -10) || ( yMul > 10 )){ 1394 yMul = 1.0 ; // set to default 1395 } 1396 } 1397 i = req.indexOf("?mulaz="); 1398 if (i != -1){ // have a request to set Axis Multiplier 1399 zMul = req.substring(i+7).toFloat() ; 1400 if (( zMul < -10) || ( zMul > 10 )){ 1401 zMul = 1.0 ; // set to default 1402 } 1403 } 1404 i = req.indexOf("?xyswp="); 1405 if (i != -1){ // have a request to set the time zone 1406 iXYS = req.substring(i+7).toInt() ; 1407 if (( iXYS < 0) || ( iXYS > 1 )){ 1408 iXYS = 0 ; 1409 } 1410 } 1411 i = req.indexOf("?nisht="); 1412 if (i != -1){ // have a request to set the time zone 1413 iNightShutdown = req.substring(i+7).toInt() ; 1414 if (( iNightShutdown < 0) || ( iNightShutdown > 1 )){ 1415 iNightShutdown = 0 ; 1416 } 1417 } 1418 1419 1420 1421 Target.flush(); 1422 Target.println(F("HTTP/1.1 200 OK")); 1423 Target.println(F("Content-Type: text/html")); 1424 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1425 Target.println(""); // this blank line be important 1426 Target.println(F("<!DOCTYPE HTML>")); 1427 Target.println(F("<head><title>Team Trouble - Solar Tracker</title>")); 1428 Target.println(F("<meta name=viewport content='width=320, auto inital-scale=1'>")); 1429 1430 Target.print(F("</head><body><html><center><h2>")); 1431 Target.print(String(trackername).substring(0,16)); 1432 Target.print(F(" Solar Tracker</h2>")); 1433 1434 if (hasRTC){ 1435 DS3231_get(&td); 1436 } 1437 Target.println(F("<b>Clocks</b>")) ; 1438 Target.println(F("<table border=1 title='Clocks'>")); 1439 Target.println(F("<tr><th>Clock Source</th><th>Time</th></tr>")); 1440 Target.print(F("<tr><td>Dallas RTC</td><td>")); 1441 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d<br>", td.year, td.mon, td.mday , tc.hour, tc.min, tc.sec); 1442 Target.print(buff) ; 1443 sendTDTREnd(); 1444 Target.println(F("</td></tr>")); 1445 Target.print(F("<tr><td>Last NTP</td><td>")); 1446 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d<br>", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 1447 Target.print(buff) ; 1448 sendTDTREnd(); 1449 Target.print(F("<tr><td>Last GPS</td><td>")); 1450 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d<br>", tg.year, tg.mon, tg.mday , tg.hour, tg.min, tg.sec); 1451 Target.print(buff) ; 1452 sendTDTREnd(); 1453 Target.print(F("<tr><td><b>Arduino Time</b></td><td><b>")); 1454 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", year(), month(), day() , hour(), minute(), second()); 1455 Target.print(buff) ; 1456 sendTDTREnd(); 1457 Target.println(F("</table>")); 1458 1459 Target.println(F("<br><b>Tracker Control System</b>")) ; 1460 1461 Target.println(F("<table border=1 title='Tracker Control'>")); 1462 Target.println(F("<tr><th> Parameter</th><th>Value</th></tr>")); 1463// Target.println("<tr><td>Tracking Mode</td><td align=center>" + String(iTrackMode) + "</td></tr>" ) ; 1464 1465 Target.print(F("<form method=get action=/><tr><td>Tracking Mode</td><td align=center>")) ; 1466 Target.print(F("<select name='tmode'>")); 1467 for (i = -1 ; i < 5 ; i++ ){ 1468 Target.print(F("<option value='")); 1469 Target.print(i) ; 1470 if ( iTrackMode == i ){ 1471 Target.print(F("' SELECTED>")); 1472 }else{ 1473 Target.print(F("'>")); 1474 } 1475 Target.print(i); 1476 Target.print(" "); 1477 switch (i){ 1478 case -1: 1479 Target.print(F("Track Both Park Both")); 1480 break; 1481 case 0: 1482 Target.print(F("Track Both Park Only E/W")); 1483 break; 1484 case 1: 1485 Target.print(F("Track and Park E/W Only")); 1486 break; 1487 case 2: 1488 Target.print(F("Track and Park N/S Only")); 1489 break; 1490 case 3: 1491 Target.print(F("Dont Track Dont Park")); 1492 break; 1493 case 4: 1494 Target.print(F("Dont Track Park Both")); 1495 break; 1496 } 1497 } 1498 Target.print(F("</select>")) ; 1499 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1500 1501 Target.print(F("<form method=get action=/><tr><td>Time Zone</td><td align=center>")) ; 1502 Target.print(F("<input type='text' name='tzone' value='")); 1503 Target.print(timezone); 1504 Target.print(F("' size=6 maxlength=2>")) ; 1505 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1506 1507 Target.print(F("<form method=get action=/><tr><td>Latitude +N -S</td><td align=center>")) ; 1508 Target.print(F("<input type='text' name='mylat' value='")); 1509 Target.print(latitude,8); 1510 Target.print(F("' size=12>")) ; 1511 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1512 1513 Target.print(F("<form method=get action=/><tr><td>Longitude</td><td align=center>")) ; 1514 Target.print(F("<input type='text' name='mylon' value='")); 1515 Target.print(longitude,8); 1516 Target.print(F("' size=12>")) ; 1517 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1518 1519 Target.print(F("<form method=get action=/><tr><td>X (N/S) Axis Multiplier</td><td align=center>")) ; 1520 Target.print(F("<input type='text' name='mulax' value='")); 1521 Target.print(xMul,2); 1522 Target.print(F("' size=5>")) ; 1523 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1524 1525 Target.print(F("<form method=get action=/><tr><td>Y (E/W) Axis Multiplier</td><td align=center>")) ; 1526 Target.print(F("<input type='text' name='mulay' value='")); 1527 Target.print(yMul,2); 1528 Target.print(F("' size=5>")) ; 1529 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1530 1531 Target.print(F("<form method=get action=/><tr><td>Z (Vert) Axis Multiplier</td><td align=center>")) ; 1532 Target.print(F("<input type='text' name='mulaz' value='")); 1533 Target.print(zMul,2); 1534 Target.print(F("' size=5>")) ; 1535 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1536 1537 Target.print(F("<form method=get action=/><tr><td>X<->Y Axis Swap</td><td align=center>")) ; 1538 Target.print(F("<select name='xyswp'>")); 1539 if (iXYS == 0 ){ 1540 Target.print(F("<option value='0' SELECTED>0 Normal")); 1541 Target.print(F("<option value='1'>1 Swapped")); 1542 }else{ 1543 Target.print(F("<option value='0'>0 Normal")); 1544 Target.print(F("<option value='1' SELECTED>1 Swapped")); 1545 } 1546 Target.println(F("</select></td><td><input type='submit' value='SET'></td></tr></form>")); 1547 1548 Target.print(F("<form method=get action=/><tr><td>Night Shutdown</td><td align=center>")) ; 1549 Target.print(F("<select name='nisht'>")); 1550 if (iNightShutdown == 0 ){ 1551 Target.print(F("<option value='0' SELECTED>0 Shutdown at Night")); 1552 Target.print(F("<option value='1'>1 Always Active")); 1553 }else{ 1554 Target.print(F("<option value='0'>0 Shutdown at Night")); 1555 Target.print(F("<option value='1' SELECTED>1 Always Active")); 1556 } 1557 Target.println(F("</select></td><td><input type='submit' value='SET'></td></tr></form>")); 1558 1559 Target.println(F("<form method=get action=/><tr><td>Tracker Name</td><td align=center><input type='text' name='tname' value='")); 1560 Target.println(String(trackername)) ; 1561 Target.println(F("' size=16 maxlength=16><input type='hidden' name='dummy' value='0'></td><td><input type='submit' value='SET'></td></tr></form>")); 1562 1563 Target.println(F("<tr><td>Solar Elevation Deg</td><td align=center>")); 1564 Target.println(String(solar_el_deg,3)) ; 1565 Target.println(F("</td><td>(Deg)")); 1566 sendTDTREnd(); 1567 1568 Target.println(F("<tr><td>Solar Azomuth</td><td align=center>")); 1569 Target.println(String(solar_az_deg,3)) ; 1570 Target.println(F("</td><td>(Deg)")); 1571 sendTDTREnd(); 1572 1573 Target.println(F("<tr><td>Sunrise</td><td align=center>")); 1574 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunrise), MinSolarTime(sunrise)); 1575 Target.println(buff) ; 1576 Target.println(F("</td><td>(hh:mm)")); 1577 sendTDTREnd(); 1578 1579 Target.println(F("<tr><td>Sunset</td><td align=center>")); 1580 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunset), MinSolarTime(sunset)); 1581 Target.println(buff) ; 1582 Target.println(F("</td><td>(hh:mm)")); 1583 sendTDTREnd(); 1584 1585 Target.println(F("<tr><td>Day or Night</td><td align=center>")); 1586 if ( iDayNight == 1 ){ 1587 Target.println(F("DAY")); 1588 }else{ 1589 Target.println(F("NIGHT")); 1590 } 1591 sendTDTREnd(); 1592 1593 Target.println(F("<tr><td>GPS lock age</td><td align=center>")); 1594 if ( fixage < 10000 ) { 1595 Target.println(String(fixage)) ; 1596 }else{ 1597 Target.println(F("-- No Lock --")) ; 1598 } 1599 Target.println(F("</td><td>(ms)")); 1600 sendTDTREnd(); 1601 1602 Target.println(F("<tr><td>GPS Satellites</td><td align=center>")); 1603 if ( gps.satellites() == 255 ){ 1604 Target.println(F("-- No Lock --")) ; 1605 }else{ 1606 Target.println(String(gps.satellites())) ; 1607 } 1608 sendTDTREnd(); 1609 1610 Target.println(F("<tr><td>GPS Chars</td><td align=center>")); 1611 Target.println(String(gpschars)) ; 1612 sendTDTREnd(); 1613 1614 Target.println(F("<tr><td>RTC Temperature</td><td align=center>")); 1615 Target.println(String(T,2)) ; 1616 Target.println(F("</td><td>(C)")); 1617 sendTDTREnd(); 1618 1619 Target.println(F("<tr><td>P/T temp</td><td align=center>")); 1620 Target.println(String(gT)) ; 1621 Target.println(F("</td><td>(C)")); 1622 sendTDTREnd(); 1623 1624 Target.println(F("<tr><td>Pressue</td><td align=center>")); 1625 Target.println(String(Pr)) ; 1626 Target.println(F("</td><td>(mBar)")); 1627 sendTDTREnd(); 1628 1629 Target.println(F("<tr><td>Gyro X</td><td align=center>")); 1630 Target.println(String(xRoll)) ; 1631 sendTDTREnd(); 1632 1633 Target.println(F("<tr><td>Gyro Y</td><td align=center>")); 1634 Target.println(String(yRoll)) ; 1635 sendTDTREnd(); 1636 1637 Target.println(F("<tr><td>Gyro Z</td><td align=center>")); 1638 Target.println(String(zRoll)) ; 1639 sendTDTREnd(); 1640 Target.println(F("</table><br>")); 1641 1642 Target.println(F("<table border=1 title='Stracker Status'>")); 1643 Target.println(F("<tr><th>Parameter</th><th>E/W Value</th><th>.</th><th>N/S Value</th><th>.</th></tr>")); 1644 1645 Target.print(F("<tr><td>Min Angle</td><td><form method=get action=/><input type='text' name='minay' value='")); 1646 Target.print(String(yMinVal)); 1647 Target.print(F("' size=12></td><td><input type='submit' value='SET'></form></td><td><form method=get action=/><input type='text' name='minax' value='")); 1648 Target.print(String(xMinVal)); 1649 Target.println(F("' size=12></td><td><input type='submit' value='SET'></form></td></tr>")) ; 1650 1651 Target.print(F("<tr><td>Max Angle</td><td><form method=get action=/><input type='text' name='maxay' value='")); 1652 Target.print( String(yMaxVal)); 1653 Target.print(F("' size=12></td><td><input type='submit' value='SET'></form></td><td><form method=get action=/><input type='text' name='maxax' value='")); 1654 Target.print( String(xMaxVal)); 1655 Target.println(F("' size=12></td><td><input type='submit' value='SET'></form></td></tr>")) ; 1656 1657 Target.print(F("<tr><td>Park Angle</td><td><form method=get action=/><input type='text' name='paray' value='")); 1658 Target.print(String(dyPark)); 1659 Target.print(F("' size=12></td><td><input type='submit' value='SET'></form></td><td><form method=get action=/><input type='text' name='parax' value='")); 1660 Target.print(String(dxPark)); 1661 Target.println(F("' size=12></td><td><input type='submit' value='SET'></form></td></tr>")) ; 1662 1663 Target.print(F("<tr><td>Offest Angle</td><td><form method=get action=/><input type='text' name='offay' value='")); 1664 Target.print(String(yzOffset)); 1665 Target.print(F("' size=12></td><td><input type='submit' value='SET'></form></td><td><form method=get action=/><input type='text' name='offax' value='")); 1666 Target.print(String(xzOffset)); 1667 Target.println(F("' size=12></td><td><input type='submit' value='SET'></form></td></tr>")) ; 1668 1669 Target.print(F("<tr><td>Hysteris Angle</td><td><form method=get action=/><input type='text' name='hysay' value='")); 1670 Target.print(String(yzH)); 1671 Target.print(F("' size=12></td><td><input type='submit' value='SET'></form></td><td><form method=get action=/><input type='text' name='hysax' value='")); 1672 Target.print(String(xzH)); 1673 Target.println(F("' size=12></td><td><input type='submit' value='SET'></form></td></tr>")) ; 1674 1675 if ( iTrackMode == 3 ){ 1676 Target.print(F("<tr><td>Target Angle</td><td><form method=get action=/><input type='text' name='taray' value='")); 1677 Target.print(String(yzTarget)); 1678 Target.print(F("' size=12></td><td><input type='submit' value='SET'></form></td><td><form method=get action=/><input type='text' name='tarax' value='")); 1679 Target.print(String(xzTarget)); 1680 Target.println(F("' size=12></td><td><input type='submit' value='SET'></form></td></tr>")) ; 1681 }else{ 1682 Target.print(F("<tr><td>Target Angle</td><td>")); 1683 Target.print(String(yzTarget)); 1684 Target.print(F("</td><td>(Deg)</td><td>")) ; 1685 Target.print(String(xzTarget)); 1686 Target.println(F("</td><td>(Deg)</td></tr>")) ; 1687 } 1688 1689 Target.print(F("<tr><td>Current Sensor Angle</td><td>")); 1690 Target.print(String(yzAng)); 1691 Target.print(F("</td><td>(Deg)</td><td>")) ; 1692 Target.print(String(xzAng)); 1693 Target.println(F("</td><td>(Deg)</td></tr>")) ; 1694 1695 Target.println(F("</table><br>")) ; 1696 1697// Target.println("<br><br>"); 1698// Target.println("<br><br>"); 1699 if ( hasSD ) { 1700 Target.println(F("<br><br><a href='index.htm'>Data Logs Files</a><br>")) ; 1701 Target.println(F("<a href='stats.htm'>Last 24 Hours Stats</a><br>")) ; 1702 } 1703 Target.println(F("<a href='/gpio/t'>Force NTP request</a><br>")) ; 1704 Target.println(F("<a href='/gpio/S'>Save Parameters to EEPROM</a><br>")) ; 1705 Target.println(F("<a href='/gpio/R'>Load Parameters from EEPROM</a><br>")) ; 1706 Target.println(F("</body></html>")); 1707 break; 1708 } 1709 } 1710 1711 delay(1); // give the web browser time to receive the data 1712 Target.stop(); // close the connection: 1713 Serial.println("Target disconnected"); 1714} 1715 1716 1717void printDirectory(File dir, int numTabs, EthernetClient Target, String MyFolder) { 1718File entry ; 1719long lTB ; 1720// Serial.println("Called " + String(numTabs)); 1721 if( numTabs == 0 ) { 1722 lTB = 0 ; 1723 dir.rewindDirectory(); // start at the begining 1724 Target.println("<table border=0>"); 1725 } 1726 while(entry = dir.openNextFile()) { // single = very subtle assign and test 1727// Serial.println(entry.name()); 1728 if (entry.isDirectory()) { 1729 MyFolder = String("/") + entry.name() ; 1730 Target.println("<tr><td><b>" + MyFolder + "</td></tr>" ); 1731 printDirectory(entry, numTabs+1,Target, MyFolder); 1732// Serial.println("Returned "); 1733 }else{ 1734 Target.print("<tr><td><A href='"); 1735 Target.print(MyFolder+ "/" + entry.name()); 1736 Target.print("'>"); 1737 Target.print(entry.name()); 1738 Target.print("</a></td><td> </td><td align=right>"); 1739 Target.print(entry.size(), DEC); 1740 lTB += entry.size() ; 1741 Target.println("</td></tr>"); 1742 } 1743 } 1744 if( numTabs == 0 ) { 1745 Target.println("<tr><td> </td></tr>"); 1746 Target.print("<tr><td><b>Total Bytes</td><td> </td><td align=right><b>"); 1747 Target.print(lTB, DEC); 1748 Target.println("</td></tr>"); 1749 Target.println("</table>"); 1750 } 1751} 1752 1753int SendFile (String FileName , EthernetClient Target ){ 1754 File dataFile = SD.open(FileName); 1755 if (dataFile) { // if the file is available, write to it: 1756 while (dataFile.available()) { 1757 Target.write(dataFile.read()); 1758 } 1759 dataFile.close(); 1760 }else { // if the file isn't open, pop up an error: 1761 Target.println("error opening " + FileName); 1762 } 1763} 1764 1765 1766float getPressure(double* Temp) 1767{ 1768 char status; 1769 double T,P; 1770 1771 status = pressure.startTemperature(); 1772 if (status != 0){ 1773 delay(status); // Wait for the measurement to complete: 1774 status = pressure.getTemperature(*Temp); 1775 if (status != 0){ 1776 status = pressure.startPressure(3); 1777 if (status != 0){ 1778 // Wait for the measurement to complete: 1779 delay(status); 1780 status = pressure.getPressure(P,*Temp); 1781 if (status != 0){ 1782 return(float(P)); 1783 } 1784 } 1785 } 1786 } 1787} 1788 1789
TRACKER_MEGA_TOP_GPS_PWM_SOFT.ino
c_cpp
Tracker for Ethernet shield uses both GPS and NTP to retrieve time
1#include <SFE_BMP180.h> 2#include <avr/wdt.h> 3#include <Wire.h> 4#include <SPI.h> 5#include <LSM303.h> // modified ... fixed a couple of bugs 6#include <LiquidCrystal_I2C.h> 7#include <L3G.h> 8#include "ds3231.h" 9#include <TimeLib.h> 10#include "ht16k33.h" 11#include <ModbusRtu.h> // Modified ... this no longer a stock lib - Slave supports register translation/mapping 12#include <EEPROM.h> 13#include <math.h> 14#include <Ethernet.h> 15#include <SD.h> 16#include <EthernetUdp.h> 17#include <TinyGPS.h> 18 19#define ID 1 20 21#define BUFF_MAX 32 22#define PARK_EAST 1 23#define PARK_WEST 2 24#define PARK_NORTH 3 25#define PARK_SOUTH 4 26#define PARK_FLAT 5 27 28#define MOTOR_DWELL 100 29 30#define MAX_MODBUS_DATA 70 31 32#define HT16K33_DSP_NOBLINK 0 // constants for the half arsed cheapo display 33#define HT16K33_DSP_BLINK1HZ 4 34#define HT16K33_DSP_BLINK2HZ 2 35#define HT16K33_DSP_BLINK05HZ 6 36 37 38const byte ENCODER_PINA = 2; // encoder connects - interupt pin 39const byte ENCODER_PINB = 3; // non interupt pin 40const byte ENCODER_PB = 8; 41 42const byte RELAY_YZ_DIR = 8; // DIR 1 Y+ Y- East / West Was the X+ N relay BROWN 43const byte RELAY_YZ_PWM = 7; // PWM 2 Speed East / West Was the Y- E relay ORANGE 44const byte RELAY_XZ_PWM = 6; // PWM 2 Speed North / South Was the Y+ W relay YELLOW 45const byte RELAY_XZ_DIR = 5; // DIR 1 X+ X- North / South Was the X- S relay BLUE 46 47 48const int chipSelect = 4; 49 50const byte UNUSED09 = 9; // cycle timer 26 Hz 38ms period 51const byte UNUSED10 = 10; 52const byte UNUSED11 = 11; 53const byte WATCHDOG = 12; 54 55IPAddress ip(192,168,2,177) ; 56IPAddress MyDNS(139,130,4,4) ; 57unsigned int localPort = 8888; // local port to listen for UDP packets 58EthernetServer server(80) ; 59EthernetClient client ; 60 61byte mac[] = { 0x44, 0x6f, 0x75, 0x67, 0x61, 0x6c }; // Dougal in ASCII 62char timeServer[] = "au.pool.ntp.org"; // time.nist.gov NTP server 63const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message 64byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets 65 66 67EthernetUDP Udp; // A UDP instance to let us send and receive packets over UDP 68 69static bool hasSD = false; 70static bool hasNet = false; 71static bool hasGyro = false; 72static bool hasRTC = false; 73static bool hasPres = false ; 74 75L3G gyro; 76LSM303 compass; 77LiquidCrystal_I2C lcd(0x27); // Set the LCD I2C address I modified the lib for default wiring 78 79SFE_BMP180 pressure; 80HT16K33 HT; 81 82TinyGPS gps; 83 84float ha ; 85float sunX ; 86float sunrise ; 87float sunset ; 88Modbus mb_slave(ID, 2, 0); // this is slave ID and RS-232 or USB-FTDI 89time_t chiptime ; 90uint8_t rtc_status ; 91//uint8_t time[8]; 92int motor_recycle = 0 ; 93char recv[BUFF_MAX]; 94unsigned int recv_size = 0; 95unsigned long prev_millis; 96uint8_t u8state; // machine state 97uint8_t u8query; // pointer to message query 98char buff[BUFF_MAX]; 99char trackername[18] ; 100unsigned long gpschars ; 101float heading ; // MODBUS MAP 102struct ts t; // 103struct ts td; // 104struct ts tg; // 105struct ts tc; // 106int iNightShutdown ; // 107time_t setchiptime ; // 68 if set to non zero this will trigger a time set event 108float zAng ; // 66 109float xMul = 1.0 ; // 64 110float yMul = 1.0 ; // 62 111float zMul = 1.0 ; // 60 112int iXYS = 0 ; // 59 113int iSave = 0 ; // 58 114int iDoSave = 0 ; // 57 115int iGPSLock = 0 ; // 56 116unsigned long fixage ; // 54 117float xRoll = 0.0 ; // 52 118float yRoll = 0.0 ; // 50 119float zRoll = 0.0 ; // 48 120float gT ; // 46 temp from sensor 121float Pr ; // 44 presure sensor 122float alt ; // 42 altitude from GPS 123float T; // 40 temperature of board (if has RTC) 124float xzTarget ; // 38 target for angles 125float yzTarget ; // 36 126float xzH ; // 34 hyserisis zone 127float yzH ; // 32 128float xzAng; // 30 current angles 129float yzAng; // 28 130float xzOffset; // 26 offset xz 131float yzOffset; // 24 offset yz 132float dyPark; // 22 parking position 133float dxPark; // 20 134float xMinVal ; // 18 Min and Max values X - N/S 135float xMaxVal ; // 16 136float yMinVal ; // 14 Y -- E/W 137float yMaxVal ; // 12 138float latitude; // 10 139float longitude; // 8 140int timezone; // 7 141int iDayNight ; // 6 142float solar_az_deg; // 4 143float solar_el_deg; // 2 144int iTrackMode ; // 1 145int iMode ; // 0 146 147int iPMode; 148int iPWM_YZ ; 149int iPWM_XZ ; 150int iPowerUp = 0 ; 151 152unsigned long tempus; 153int8_t state1 = 0; 154int8_t rtc_hour = 0; 155int8_t rtc_min = 0 ; 156int8_t rtc_sec = 0 ; 157 158void LoadParamsFromEEPROM(bool bLoad){ 159 if ( bLoad ) { 160 xzH = LoadFloatFromEEPROM(0,0.1,20.0,4.0); // hysterisis NS 161 yzH = LoadFloatFromEEPROM(1,0.1,20.0,4.0); // "" EW 162 163 dyPark = LoadFloatFromEEPROM(2,-70.0,50.0,0); 164 dxPark = LoadFloatFromEEPROM(3,-5.0,50.0,0.0); 165 166 xzOffset = LoadFloatFromEEPROM(4,-90.0,90.0,0); // NS 167 yzOffset = LoadFloatFromEEPROM(5,-90.0,90.0,0); // EW 168 169 xzTarget = LoadFloatFromEEPROM(6,-90.0,90.0,0); // NS 170 yzTarget = LoadFloatFromEEPROM(7,-90.0,90.0,0); // EW 171 172 xMinVal = LoadFloatFromEEPROM(8,-10.0,60.0,0.0); // NS 173 xMaxVal = LoadFloatFromEEPROM(9,-10.0,60.0,45); 174 175 yMinVal = LoadFloatFromEEPROM(10,-70.0,50.0,-65); // EW 176 yMaxVal = LoadFloatFromEEPROM(11,-70.0,50.0,45); 177 178 iTrackMode = LoadIntFromEEPROM(12,-1,4,0); 179 180 latitude = LoadFloatFromEEPROM(13,-90.0,90.0,-34.051219); 181 longitude = LoadFloatFromEEPROM(14,-180.0,180.0,142.013618); 182 timezone = LoadIntFromEEPROM(15,0,23,10); 183 xMul = LoadFloatFromEEPROM(16,-10,10,1); 184 yMul = LoadFloatFromEEPROM(17,-10,10,1); 185 zMul = LoadFloatFromEEPROM(18,-10,10,1); 186 iXYS = LoadIntFromEEPROM(19,0,1,0); 187 if ( xMul == 0.0 ) // zero is rubbish value so take 1.0 as the default 188 xMul = 1.0 ; 189 if ( yMul == 0.0 ) 190 yMul = 1.0 ; 191 if ( zMul == 0.0 ) 192 zMul = 1.0 ; 193 iNightShutdown = LoadIntFromEEPROM(20,0,1,1); 194 EEPROM.get(0 + (30 * sizeof(float)) , trackername ); 195 }else{ 196 EEPROM.put( 0 , xzH ); 197 EEPROM.put(0 + (1 * sizeof(float)) , yzH ); 198 EEPROM.put(0 + (2 * sizeof(float)) , dyPark ); 199 EEPROM.put(0 + (3 * sizeof(float)) , dxPark ); 200 EEPROM.put(0 + (4 * sizeof(float)) , xzOffset ); 201 EEPROM.put(0 + (5 * sizeof(float)) , yzOffset ); 202 EEPROM.put(0 + (6 * sizeof(float)) , xzTarget ); 203 EEPROM.put(0 + (7 * sizeof(float)) , yzTarget ); 204 EEPROM.put(0 + (8 * sizeof(float)) , xMinVal ); 205 EEPROM.put(0 + (9 * sizeof(float)) , xMaxVal ); 206 EEPROM.put(0 + (10 * sizeof(float)) , yMinVal ); 207 EEPROM.put(0 + (11 * sizeof(float)) , yMaxVal ); 208 EEPROM.put(0 + (12 * sizeof(float)) , iTrackMode ); 209 EEPROM.put(0 + (13 * sizeof(float)) , latitude ); 210 EEPROM.put(0 + (14 * sizeof(float)) , longitude ); 211 EEPROM.put(0 + (15 * sizeof(float)) , timezone ); 212 EEPROM.put(0 + (16 * sizeof(float)) , xMul ); 213 EEPROM.put(0 + (17 * sizeof(float)) , yMul ); 214 EEPROM.put(0 + (18 * sizeof(float)) , zMul ); 215 EEPROM.put(0 + (19 * sizeof(float)) , iXYS ); 216 EEPROM.put(0 + (20 * sizeof(float)) , iNightShutdown ); 217 EEPROM.put(0 + (30 * sizeof(float)) , trackername); 218 } 219} 220 221void setup() { 222 int led ; 223 224 Wire.begin(); 225 lcd.begin(20, 4); 226 lcd.home(); 227 lcd.setBacklightPin(3, NEGATIVE); 228 lcd.noCursor(); 229 lcd.clear(); 230 231 MCUSR &= ~_BV(WDRF); 232 wdt_disable(); 233 234 compass.init(); 235 compass.enableDefault(); 236 compass.setTimeout(1000); 237 238 Serial.begin(115200); // program/debug port 239 Serial1.begin(9600); // GPS port 240// Serial2.begin(115200); // Modbus Port to esp 241 Serial3.begin(115200); // EPS8266 serial converter 242 243 if (gyro.init()) { 244 gyro.enableDefault(); 245 hasGyro = true ; 246 } 247 if (pressure.begin()){ 248 Serial.println("BMP180 init success"); 249 hasPres = true ; 250 } 251 pinMode(RELAY_XZ_DIR, OUTPUT); // Outputs for PWM motor control 252 pinMode(RELAY_XZ_PWM, OUTPUT); // 253 pinMode(RELAY_YZ_PWM, OUTPUT); // 254 pinMode(RELAY_YZ_DIR, OUTPUT); // 255 iPWM_YZ = 0 ; 256 iPWM_XZ = 0 ; 257 pinMode(13, OUTPUT); // 258 digitalWrite(13, HIGH ); 259 ActivateRelays(0); // call an all stop first 260 261 mb_slave.begin( 9600 ); // RS-232 to base of tower 262 tempus = millis() + 100; 263 264 pinMode(UNUSED09, OUTPUT); // unused so dont leave floating set as output 265 pinMode(UNUSED10, OUTPUT); // 266 pinMode(UNUSED11, OUTPUT); // 267 pinMode(WATCHDOG, OUTPUT); // 268 digitalWrite(WATCHDOG,HIGH); 269 270 compass.m_min = (LSM303::vector<int16_t>) {-3848, -1822, -1551 }; // calibration figures are empirical 271 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300}; 272 273 LoadParamsFromEEPROM(true); 274 275 DS3231_init(DS3231_INTCN); // look for a rtc 276 DS3231_get(&tc); 277 DS3231_get(&td); 278 rtc_status = DS3231_get_sreg(); 279 if ((tc.mon == 0 )&& (tc.mday==0)){ // no rtc to load off 280 setTime (0,0,0,21,9,2017) ; // midnight on the equinox (will deactivae motors till gets a valid time) ; 281 }else{ 282 setTime((int)tc.hour,(int)tc.min,(int)tc.sec,(int)tc.mday,(int)tc.mon,(int)tc.year ) ; // set the internal RTC 283 hasRTC = true ; 284 } 285 286 HT.begin(0x00); 287 for (led = 0; led < 127; led++) { 288 HT.clearLedNow(led); 289 } 290 291 Serial.println("EtherNet init"); 292 if (SD.begin(chipSelect) || true){ 293 hasSD = true; 294 Serial.println("SD OK"); 295 Ethernet.begin(mac,ip,MyDNS); 296// Ethernet.begin(mac,ip); 297 if (Udp.begin(localPort)==1){ 298 hasNet = true ; 299 } 300 server.begin(); 301 } 302 Serial.print("server is at "); 303 Serial.println(Ethernet.localIP()); 304 305 306// wdt_enable(WDTO_8S); 307} 308 309// Arduino doesnt have these to we define from a sandard libruary 310float arcsin(float x) { 311 return (atan(x / sqrt(-x * x + 1))); 312} 313float arccos(float x) { 314 return (atan(x / sqrt(-x * x + 1)) + (2 * atan(1))); 315} 316// fractional orbital rotation in radians 317float gama(struct ts *tm) { 318 return ((2 * PI / 365 ) * DayOfYear(tm->year , tm->mon , tm->mday , tm->hour , tm->min )); 319} 320// equation of rime 321float eqTime(float g) { 322 return (229.18 * ( 0.000075 + ( 0.001868 * cos(g)) - (0.032077 * sin(g)) - (0.014615 * cos (2 * g)) - (0.040849 * sin(2 * g)))); 323} 324// declination of sun in radians 325float Decl(float g) { 326 return ( 0.006918 - (0.399912 * cos(g)) + (0.070257 * sin(g)) - (0.006758 * cos(2 * g)) + ( 0.000907 * sin(2 * g)) - ( 0.002697 * cos(3 * g)) + (0.00148 * sin(3 * g)) ); 327} 328float TimeOffset(float longitude , struct ts *tm , int timezone ) { 329 float dTmp ; 330 dTmp = (-4.0 * longitude ) + (60 * timezone) - eqTime(gama(tm)) ; 331 return (dTmp); 332} 333 334float TrueSolarTime(float longitude , struct ts *tm , int timezone ) { 335 float dTmp ; 336 dTmp = ( 60.0 * tm->hour ) + (1.0 * tm->min) + (1.0 * tm->sec / 60) - TimeOffset(longitude, tm, timezone) ; 337 return (dTmp); 338} 339float HourAngle(float longitude , struct ts *tm , int timezone) { 340 float dTmp; 341 dTmp = (TrueSolarTime(longitude, tm, timezone) / 4 ) - 180 ; // 720 minutes is solar noon -- div 4 is 180 342 return (dTmp); 343} 344// Hour angle for sunrise and sunset only 345float HA (float lat , struct ts *tm ) { 346 float latRad ; 347 latRad = lat * 2 * PI / 360 ; 348 return ( acos((cos(90.833 * PI / 180 ) / ( cos(latRad) * cos(Decl(gama(tm)))) - (tan(latRad) * tan(Decl(gama(tm)))))) / PI * 180 ); 349} 350 351float Sunrise(float longitude , float lat , struct ts *tm , int timezone) { 352 return (720 - ( 4.0 * (longitude + HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 353} 354float Sunset(float longitude , float lat , struct ts *tm , int timezone) { 355 return (720 - ( 4.0 * (longitude - HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 356} 357float SNoon(float longitude , float lat , struct ts *tm , int timezone) { 358 return (720 - ( 4.0 * (longitude + (60 * timezone) - eqTime(gama(tm)))) ) ; 359} 360 361float SolarZenithRad(float longitude , float lat , struct ts *tm , int timezone) { 362 float latRad ; 363 float decRad ; 364 float HourAngleRad ; 365 float dTmp ; 366 367 latRad = lat * 2 * PI / 360 ; 368 decRad = Decl(gama(tm)); 369 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 370 dTmp = acos((sin(latRad) * sin(decRad)) + (cos(latRad) * cos(decRad) * cos(HourAngleRad))); 371 return (dTmp) ; 372 373} 374float SolarElevationRad(float longitude , float lat , struct ts *tm , int timezone ) { 375 return ((PI / 2) - SolarZenithRad(longitude , lat , tm , timezone )) ; 376} 377 378float SolarAzimouthRad(float longitude , float lat , struct ts *tm , int timezone) { 379 float latRad ; 380 float decRad ; 381 float solarzenRad ; 382 float HourAngleRad ; 383 float dTmp ; 384 latRad = lat * 2 * PI / 360 ; 385 decRad = Decl(gama(tm)); 386 solarzenRad = SolarZenithRad ( longitude , lat , tm , timezone ) ; 387 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 388 dTmp = acos(((sin(decRad) * cos(latRad)) - (cos(HourAngleRad) * cos(decRad) * sin(latRad))) / sin(solarzenRad)) ; 389 if ( HourAngleRad < 0 ) { 390 return (dTmp) ; 391 } else { 392 return ((2 * PI) - dTmp) ; 393 } 394} 395 396 397void StopYZ(){ 398 iPWM_YZ=0 ; 399 motor_recycle = MOTOR_DWELL ; 400} 401void StopXZ(){ 402 iPWM_XZ=0 ; 403 motor_recycle = MOTOR_DWELL ; 404} 405 406void ActivateRelays(int iAllStop) { 407 if (motor_recycle > 0 ){ 408 motor_recycle-- ; 409 } 410 if ( iAllStop == 0 ) { 411 StopYZ() ; 412 StopXZ() ; 413 } else { 414 if (( iPWM_YZ==0 ) && (motor_recycle == 0 )){ 415 if (((yzAng ) < ( yzTarget - yzH )) ) { // do Y ie E/W before N/S 416 digitalWrite(RELAY_YZ_DIR, LOW) ; 417 iPWM_YZ=2 ; 418 } 419 if (((yzAng ) > (yzTarget + yzH )) ) { 420 digitalWrite(RELAY_YZ_DIR, HIGH) ; 421 iPWM_YZ=2 ; 422 } 423 } 424 if ( iPWM_YZ>0 ){ 425 if ((yzAng > yzTarget) && ( digitalRead(RELAY_YZ_DIR)==LOW )) { 426 StopYZ() ; 427 } 428 if ((yzAng < yzTarget) && ( digitalRead(RELAY_YZ_DIR)==HIGH )) { 429 StopYZ() ; 430 } 431 } 432 433 if (( iPWM_YZ==0)) { // if finished on E/W you can do N/S 434 if (( iPWM_XZ==0 ) && (motor_recycle == 0 )){ 435 if ((xzAng < ( xzTarget - xzH )) ) { // turn on if not in tolerance 436 digitalWrite(RELAY_XZ_DIR, LOW) ; 437 iPWM_XZ=2 ; 438 } 439 if ((xzAng > ( xzTarget + xzH )) ) { // turn on if not in tolerance 440 digitalWrite(RELAY_XZ_DIR, HIGH) ; 441 iPWM_XZ=2 ; 442 } 443 } 444 }else{ 445 if ((iPWM_XZ>0 )){ 446 StopXZ() ; 447 } 448 } 449 if ( iPWM_XZ>0 ){ 450 if ((xzAng > xzTarget ) && ( digitalRead(RELAY_XZ_DIR)==LOW )) { // if on turn off 451 StopXZ() ; 452 } 453 if ((xzAng < xzTarget ) && ( digitalRead(RELAY_XZ_DIR)==HIGH )) { // if on turn off 454 StopXZ() ; 455 } 456 } 457 } 458 if (iPWM_XZ>0){ 459 iPWM_XZ += 3 ; 460 } 461 if (iPWM_YZ>0){ 462 iPWM_YZ += 3 ; 463 } 464 iPWM_XZ = constrain(iPWM_XZ,0,254); 465 iPWM_YZ = constrain(iPWM_YZ,0,254); 466 analogWrite(RELAY_XZ_PWM,iPWM_XZ); 467 analogWrite(RELAY_YZ_PWM,iPWM_YZ); 468} 469 470void FloatToModbusWords(float src_value , uint16_t * dest_lo , uint16_t * dest_hi ) { 471 uint16_t tempdata[2] ; 472 float *tf ; 473 tf = (float * )&tempdata[0] ; 474 *tf = src_value ; 475 *dest_lo = tempdata[1] ; 476 *dest_hi = tempdata[0] ; 477} 478float FloatFromModbusWords( uint16_t dest_lo , uint16_t dest_hi ) { 479 uint16_t tempdata[2] ; 480 float *tf ; 481 tf = (float * )&tempdata[0] ; 482 tempdata[1] = dest_lo ; 483 tempdata[0] = dest_hi ; 484 return (*tf) ; 485} 486 487 488float LoadFloatFromEEPROM(int address,float minval,float maxval, float defaultval){ 489float tmp ; 490 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 491 if (( tmp < minval ) || ( tmp > maxval )|| (NumberOK(tmp) == 1)) { 492 tmp = defaultval ; 493 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 494 } 495 return(tmp); 496} 497int LoadIntFromEEPROM(int address,int minval,int maxval, int defaultval){ 498int tmp ; 499 EEPROM.get(0 + (address * sizeof(float)) , tmp ); // float.. yeah yeah I know... but it makes it compatible with the one above (easy on the brain) 500 if (( tmp < minval ) || ( tmp > maxval )) { 501 tmp = defaultval ; 502 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 503 } 504 return(tmp); 505} 506 507int NumberOK (float target) { 508 int tmp = 0 ; 509 tmp = isnan(target); 510 if ( tmp != 1 ) { 511 tmp = isinf(target); 512 } 513 return (tmp); 514} 515 516int SetTimeFromGPS(){ 517 byte hundredths ; 518 519 gps.crack_datetime((int *)&tg.year,(byte *)&tg.mon,(byte *) &tg.mday,(byte *) &tg.hour,(byte *) &tg.min,(byte *) &tg.sec , &hundredths, &fixage); 520 setTime((int)tg.hour,(int)tg.min,(int)tg.sec,(int)tg.mday,(int)tg.mon,(int)tg.year ) ; // set the internal RTC from last GPS time 521 chiptime = now() ; // get it back again 522 chiptime += (( timezone * SECS_PER_HOUR ) + ( fixage / 1000 )) ; // add the offset plus the fix age 523 setTime(chiptime); // set it again 524 if (hasRTC) { 525 tg.year = year(); 526 tg.mon = month() ; 527 tg.mday = day(); 528 tg.hour = hour() ; 529 tg.min = minute(); 530 tg.sec = second(); 531 DS3231_set(tg); //should also update this 532 } 533 return(0); 534} 535 536 537void loop() { 538 float P; 539 float sunInc; 540 float sunAng; 541 float xzRatio; 542 float yzRatio; 543 float decl ; 544 float eqtime ; 545 float dTmp ; 546 float heading ; 547 float tst ; 548 float flat, flon; 549 unsigned short goodsent; 550 unsigned short failcs; 551// int iYear , iMon , iMday , iHour , iMin , iSec ; 552 553 554 if (minute() != rtc_min) { // do onlyonce a minute 555 gps.stats(&gpschars, &goodsent , &failcs ); 556 gps.f_get_position(&flat, &flon,(long unsigned *) &fixage); // return in degrees 557 if (hasPres){ 558 Pr = getPressure((double *)&gT) ; 559 } 560 if (hasRTC) { 561 T = DS3231_get_treg(); 562 } 563 rtc_min = minute() ; 564 565 if ((fixage > 0 ) && ( fixage < 40000 )) { // wait till our fix is valid before we use the values 566 latitude = flat ; 567 longitude = flon ; 568 iGPSLock = gps.satellites() ; 569 alt = gps.f_altitude() ; 570 if (iPowerUp==0) { // only do this at startup so we have a better position ref for next time 571 EEPROM.put(0 + (13 * sizeof(float)) , latitude ); 572 EEPROM.put(0 + (14 * sizeof(float)) , longitude ); 573 iPowerUp = 1 ; 574 if (!hasNet ){ 575 SetTimeFromGPS(); 576 } 577 } 578 }else{ 579 iGPSLock = 0 ; // if no lock loook at internal clock 580 } 581 } 582 tc.year = year(); 583 tc.mon = month() ; 584 tc.mday = day(); 585 tc.hour = hour() ; 586 tc.min = minute(); 587 tc.sec = second(); 588 589 compass.read(); // this reads all 6 channels 590 if ( hasGyro ){ 591 gyro.read(); 592 xRoll = gyro.g.x ; 593 yRoll = gyro.g.y ; 594 zRoll = gyro.g.z ; 595 } 596 597 digitalWrite(UNUSED09,!digitalRead(UNUSED09)); // toggle this output so I can measure the cycle time with a scope 598 599 heading = compass.heading((LSM303::vector<int>) { 1, 0, 0 }); 600 601 if (( compass.a.z != 0) && (!compass.timeoutOccurred() )) { 602 zAng = (float)compass.a.z ; 603 if (iXYS == 0 ){ // Proper Job make it configurable 604 xzRatio = (float)compass.a.x * xMul / abs(zAng) ; // Normal 605 yzRatio = (float)compass.a.y * yMul / abs(zAng) ; 606 }else{ 607 xzRatio = (float)compass.a.y * xMul / abs(zAng) ; // Swapped 608 yzRatio = (float)compass.a.x * yMul / abs(zAng) ; 609 } 610 xzAng = ((float)atan(xzRatio) / PI * 180 ) + xzOffset ; // good old offsets or fudge factors 611 yzAng = ((float)atan(yzRatio) / PI * 180 ) + yzOffset ; 612// digitalWrite(13, LOW); 613 }else{ // try restarting the compass/accelerometer modual - cos he gone walkabout... 614 Wire.begin(); // reset the I2C 615 compass.init(); 616 compass.enableDefault(); 617 compass.setTimeout(1000); // BTW I fixed up the int / long issue in the time out function in the LM303 lib I was using 618 compass.m_min = (LSM303::vector<int16_t>) {-3848, -1822, -1551 }; // calibration figures are empirical (just whirl it around a bit and records the min max !!) 619 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300 }; 620/* if (tc.sec % 2 == 0 ) { 621 digitalWrite(13, HIGH); 622 } else { 623 digitalWrite(13, LOW); 624 }*/ 625 HT.begin(0x00); 626 } 627 628 if (setchiptime > 0) { // update the arduino time from the modbus register then clear it... also set RTC if fitted 629 setTime(setchiptime) ; 630 if (hasRTC) { 631 tc.year = year(); 632 tc.mon = month() ; 633 tc.mday = day(); 634 tc.hour = hour() ; 635 tc.min = minute(); 636 tc.sec = second(); 637 DS3231_set(tc); //should also update this 638 } 639 setchiptime = 0 ; 640 } 641 642 if ( hour() != rtc_hour){ // update our time every hour if we can 643 if (iGPSLock == 0){ 644 if( hasNet ){ 645 sendNTPpacket(timeServer); // send an NTP packet to a time server every hour 646 }else{ 647 if (hasRTC) { 648 DS3231_get(&td); 649 setTime((int)td.hour,(int)td.min,(int)td.sec,(int)td.mday,(int)td.mon,(int)td.year ) ; // set the internal RTC from Dallas RTC 650 } 651 } 652 }else{ 653 if ((fixage > 0 ) && ( fixage < 10000 )) { // if the lock is less than 10 second old 654 SetTimeFromGPS(); 655 } 656 } 657 rtc_hour = hour() ; 658 } 659 660 if ( rtc_sec != second() ) { //only update once a second 661 wdt_reset(); // reset internal watchdog - good puppy 662 663 if (( tc.sec > 8 ) && ( tc.sec < 58 )) { // dont calculate arround the minute when time is updating from NTP or GPS as might get a not so funny result 664 digitalWrite(13,!digitalRead(13)); 665 solar_az_deg = SolarAzimouthRad(longitude, latitude, &tc, timezone) * 180 / PI ; 666 solar_el_deg = SolarElevationRad(longitude, latitude, &tc, timezone) * 180 / PI ; 667 668 decl = Decl(gama(&tc)) * 180 / PI ; 669 ha = HourAngle (longitude , &tc , timezone ) ; 670 sunrise = Sunrise(longitude, latitude, &tc, timezone) ; 671 sunset = Sunset(longitude, latitude, &tc, timezone); 672 tst = TrueSolarTime(longitude, &tc, timezone); 673 sunX = abs(latitude) + decl ; 674 if (solar_el_deg >= 0 ){ // day 675 iDayNight = 1 ; 676 }else{ // night 677 iDayNight = 0 ; 678 } 679 } 680 switch (iTrackMode) { 681 case 4: // both axis to park 682 yzTarget = dyPark ; // night park position E/W 683 xzTarget = dxPark ; // night park position N/S 684 break ; 685 case 3: // both axis off no tracking 686 break ; 687 case 2: // xz tracking NS 688 if ( iDayNight == 1 ) { 689 xzTarget = sunX ; // need to map the coordinate system correctly 690 } else { 691 xzTarget = dxPark ; // night park position 692 } 693 break; 694 case 1: // yz tracking EW 695 if (iDayNight == 1) { 696 yzTarget = ha ; 697 } else { 698 yzTarget = dyPark ; // night park position 699 } 700 break; 701 case -1: // set target to tracking and park both at nigh 702 if (iDayNight == 1) { 703 yzTarget = ha ; 704 xzTarget = sunX ; // need to map the coordinate system correctly 705 } else { 706 yzTarget = dyPark ; // night park position E/W 707 xzTarget = dxPark ; // night park position N/S 708 } 709 break; 710 default: // set target to tracking 711 if (iDayNight == 1) { 712 yzTarget = ha ; 713 xzTarget = sunX ; // need to map the coordinate system correctly 714 } else { 715 yzTarget = dyPark ; // night park position (dont park the other - leave till morning) 716 } 717 break; 718 } 719 xzTarget = constrain(xzTarget,xMinVal,xMaxVal); // constain function... very cool - dont leave home without it ! 720 yzTarget = constrain(yzTarget,yMinVal,yMaxVal); 721 722 lcd.setCursor ( 0, 0 ); // Diags in case there is an LCD display attached 723 lcd.print("X/Z "); 724 if ( xzAng > 0 ) { 725 lcd.print("+"); 726 } 727 lcd.print(xzAng); 728 lcd.setCursor ( 10, 0 ); 729 lcd.print("Y/Z "); 730 if ( yzAng > 0 ) { 731 lcd.print("+"); 732 } 733 lcd.print(yzAng); 734 lcd.setCursor ( 0, 1 ); 735 lcd.print("TX "); 736 if (( xzTarget) > 0 ) { 737 lcd.print("+"); 738 } 739 lcd.print(( xzTarget)); 740 lcd.setCursor ( 10, 1 ); 741 lcd.print("TY "); 742 if (( yzTarget) > 0 ) { 743 lcd.print("+"); 744 } 745 lcd.print(( yzTarget)); 746 lcd.setCursor ( 0, 2 ); 747 lcd.print("DX "); 748 dTmp = ( xzAng - xzTarget) ; 749 if (dTmp > 0 ) { 750 lcd.print("+"); 751 } 752 lcd.print(dTmp); 753 lcd.setCursor ( 10, 2 ); 754 lcd.print("DY "); 755 dTmp = ( yzAng - yzTarget) ; 756 if (dTmp > 0 ) { 757 lcd.print("+"); 758 } 759 lcd.print(dTmp); 760 761 lcd.setCursor ( 0, 3 ); // line 3 762 snprintf(buff, BUFF_MAX, "%02d:%02d:%02d", tc.hour, tc.min, tc.sec); 763 lcd.print(buff) ; 764 765 lcd.setCursor ( 9, 3 ); // line 3 766 lcd.print( "S") ; 767 if ( iDayNight == 0 ) { 768 lcd.print( "-") ; 769 }else{ 770 if (( tc.sec % 2 ) == 0 ) { 771 lcd.print( "<") ; 772 }else{ 773 lcd.print( ">") ; 774 } 775 } 776 if ( gps.satellites() > 9 ){ 777 lcd.print( "-" ); 778 }else{ 779 lcd.print(gps.satellites()) ; 780 } 781 782 lcd.setCursor ( 13, 3 ); // line 3 783 snprintf(buff, BUFF_MAX, "%04X", fixage); 784 lcd.print(buff) ; 785 786 lcd.setCursor ( 18, 3 ); // line 3 787 if ( iPWM_YZ == 0 ) { 788 lcd.print( " ") ; 789 }else{ 790 if (( digitalRead(RELAY_YZ_DIR) == LOW )) { 791 lcd.print( "W") ; 792 }else{ 793 lcd.print( "E") ; 794 } 795 } 796 lcd.setCursor ( 19, 3 ); // line 3 797 if ( iPWM_XZ == 0 ) { 798 lcd.print( " ") ; 799 }else{ 800 if (( digitalRead(RELAY_XZ_DIR) == LOW )) { 801 lcd.print( "N") ; 802 }else{ 803 lcd.print( "S") ; 804 } 805 } 806 807 rtc_sec = second() ; 808 DisplayMeatBall() ; 809 } 810 811 if ( iDoSave == 2 ) { // save them Active via web or 812 LoadParamsFromEEPROM(false); 813 iDoSave = 0 ; // only do once 814 } 815 if ( iDoSave == 3 ) { // load them 816 LoadParamsFromEEPROM(true); 817 iDoSave = 0 ; // only do once 818 } 819 820 if (((tc.hour > 19 ) || ( tc.hour < 5 )) && (iTrackMode < 3)) { 821 if ( iNightShutdown != 0 ){ 822 ActivateRelays(1) ; 823 }else{ 824 ActivateRelays(0) ; // power down at night if in tracking mode 825 } 826 }else{ 827 ActivateRelays(1) ; 828 } 829 830 831 state1 = mb_slave.poll( (uint16_t*)&iMode, MAX_MODBUS_DATA ); 832 833 switch (state1) { 834 case EXC_ADDR_RANGE: 835 Serial.println("EXC_ADDR_RANGE PORT 2"); 836 break; 837 case EXC_FUNC_CODE: 838 Serial.println("EXC_FUNC_CODE PORT 2"); 839 break; 840 case EXC_REGS_QUANT: 841 Serial.println("EXC_REGS_QUANT PORT 2"); 842 break; 843 } 844 845 if ( hasNet ) { 846 if ( Udp.parsePacket() ) { // check if NTP packet arrived back 847 processNTPpacket(); 848 } 849 850 client = server.available(); // process web server 851 if (client) { 852 processTCPClient(client); 853 } 854 } 855 856 while (Serial1.available()){ // process the gps buffer 857 gps.encode(Serial1.read()); 858 } 859} // end of loop 860 861 862 863 864 865 866float DayOfYear(uint16_t iYear , uint8_t iMon , uint8_t iDay , uint8_t iHour , uint8_t iMin ) { 867 int i ; 868 float iTDay ; 869 870 iTDay = iDay - 1 ; // this is zero referenced 871 for ( i = 1 ; i < iMon ; i++ ) { 872 switch (i) { 873 case 1: 874 case 3: 875 case 5: 876 case 7: 877 case 8: 878 case 10: 879 case 12: 880 iTDay += 31 ; 881 break; 882 case 4: 883 case 6: 884 case 9: 885 case 11: 886 iTDay += 30 ; 887 break; 888 case 2 : 889 if ((iYear % 4) == 0 ) { 890 iTDay += 29 ; 891 } else { 892 iTDay += 28 ; 893 } 894 break; 895 } 896 } 897 iTDay += (( 1.0 * iHour - 12 ) / 24 ) ; 898 // iDay += 1.0 * iMin / 1440 ; 899 return (iTDay); 900} 901 902 903int HrsSolarTime(float target) { 904 int i ; 905 i = target ; 906 return ( i / 60 ); 907} 908int MinSolarTime(float target) { 909 int i ; 910 i = target ; 911 return ( i % 60 ); 912} 913 914float sign(float target) { 915 if (target > 0 ) { 916 return (1); 917 } else { 918 if (target < 0 ) { 919 return (-1); 920 } else { 921 return (0); 922 } 923 } 924} 925 926void DisplayMeatBall() { 927 int pos , led , x , y; 928 float dx , dy ; 929 float dxa , dya ; 930 931 HT.setBrightness(15); 932 933 dx = xzAng - xzTarget ; 934 dy = yzAng - yzTarget ; 935 dxa = abs(dx) ; 936 dya = abs(dy) ; 937 if (dxa < 6) { 938 x = 0 ; 939 } else { 940 if (dxa < 12) { 941 x = sign(dx); 942 } else { 943 if (dxa < 50) { 944 x = 2 * sign(dx); 945 } else { 946 x = 3 * sign(dx); 947 } 948 } 949 } 950 if (dya < 6) { 951 y = 0 ; 952 } else { 953 if (dya < 12) { 954 y = sign(dy); 955 } else { 956 if (dya < 25) { 957 y = 2 * sign(dy); 958 } else { 959 y = 3 * sign(dy); 960 } 961 } 962 } 963 pos = 27 ; // netral position 964 pos += (y * 8) ; // add or sumtract the x in range of -3 to +3 965 pos += (x ) ; // add or sumtract 8 * y or y in range of -3 to +3 966 for (led = 0; led < 63; led++) { 967 switch (led){ 968 case 0: 969 case 7: 970 case 56: 971 case 63: 972 break; 973 default: 974 HT.clearLedNow(MapLedNo(led)); 975 break; 976 } 977 } 978 979 if ( ++iPMode > 100 ) { 980 iPMode = 1 ; 981 } 982 switch(gps.satellites()){ 983 case 255: 984 HT.clearLedNow(MapLedNo(0)); // turn off four courners 985 HT.clearLedNow(MapLedNo(7)); // turn off four courners 986 HT.clearLedNow(MapLedNo(63)); // turn off four courners 987 HT.clearLedNow(MapLedNo(56)); // turn off four courners 988 break; 989 case 1: 990 case 2: 991 HT.setLedNow(MapLedNo(0)); // turn on four courners 992 HT.clearLedNow(MapLedNo(7)); // turn on four courners 993 HT.clearLedNow(MapLedNo(63)); // turn on four courners 994 HT.clearLedNow(MapLedNo(56)); // turn on four courners 995 break; 996 case 3: 997 HT.setLedNow(MapLedNo(0)); // turn on four courners 998 HT.setLedNow(MapLedNo(7)); 999 HT.clearLedNow(MapLedNo(63)); // turn on four courners 1000 HT.clearLedNow(MapLedNo(56)); // turn on four courners 1001 break; 1002 case 4: 1003 HT.setLedNow(MapLedNo(0)); // turn on four courners 1004 HT.setLedNow(MapLedNo(7)); 1005 HT.setLedNow(MapLedNo(56)); 1006 HT.clearLedNow(MapLedNo(63)); // turn on four courners 1007 break; 1008 default: 1009 HT.setLedNow(MapLedNo(0)); // turn on four courners 1010 HT.setLedNow(MapLedNo(7)); 1011 HT.setLedNow(MapLedNo(56)); 1012 HT.setLedNow(MapLedNo(63)); 1013 break; 1014 } 1015 1016 1017 // HT.setLedNow(MapLedNo(tc.sec)); 1018 if ((iPWM_YZ == 0) && (iPWM_XZ == 0)) { 1019// HT.setBlinkRate(HT16K33_DSP_NOBLINK); // not attempting to move 1020 if ((tc.sec % 2) == 0 ) { 1021 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1022 HT.setLedNow(MapLedNo(pos + 9)); 1023 }else{ 1024 HT.setLedNow(MapLedNo(pos + 1)); 1025 HT.setLedNow(MapLedNo(pos + 8)); 1026 } 1027 } else { 1028// HT.setBlinkRate(HT16K33_DSP_BLINK2HZ); //moving so blink meat ball 1029 if (( iPMode % 2 ) == 0 ){ 1030 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1031 HT.setLedNow(MapLedNo(pos + 1)); 1032 HT.setLedNow(MapLedNo(pos + 8)); 1033 HT.setLedNow(MapLedNo(pos + 9)); 1034 } 1035 } 1036} 1037 1038 1039int MapLedNo(int target) // this compensates for the screwy setup of the matrix driver to the chip 1040{ 1041 int row ; 1042 int col ; 1043 row = target / 8 ; 1044 col = target % 8 ; 1045 if (col == 0 ) { 1046 return ((row * 16 ) + 7) ; 1047 } else { 1048 return ((row * 16 ) + col - 1) ; 1049 } 1050} 1051 1052 1053unsigned long sendNTPpacket(char* address) // send an NTP request to the time server at the given address 1054{ 1055 memset(packetBuffer, 0, NTP_PACKET_SIZE); // set all bytes in the buffer to 0 1056 // Initialize values needed to form NTP request 1057 // (see URL above for details on the packets) 1058 packetBuffer[0] = 0b11100011; // LI, Version, Mode 1059 packetBuffer[1] = 0; // Stratum, or type of clock 1060 packetBuffer[2] = 6; // Polling Interval 1061 packetBuffer[3] = 0xEC; // Peer Clock Precision 1062 // 8 bytes of zero for Root Delay & Root Dispersion 1063 packetBuffer[12] = 49; 1064 packetBuffer[13] = 0x4E; 1065 packetBuffer[14] = 49; 1066 packetBuffer[15] = 52; 1067 1068 // all NTP fields have been given values, now 1069 // you can send a packet requesting a timestamp: 1070 Udp.beginPacket(address, 123); //NTP requests are to port 123 1071 Udp.write(packetBuffer, NTP_PACKET_SIZE); 1072 Udp.endPacket(); 1073} 1074 1075 1076unsigned long processNTPpacket(void){ 1077 struct ts tntp; 1078 Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer 1079 1080 //the timestamp starts at byte 40 of the received packet and is four bytes, 1081 // or two words, long. First, esxtract the two words: 1082 1083 unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); 1084 unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 1085 // combine the four bytes (two words) into a long integer 1086 // this is NTP time (seconds since Jan 1 1900): 1087 unsigned long secsSince1900 = highWord << 16 | lowWord; 1088 Serial.print(F("Seconds since Jan 1 1900 = " )); 1089 Serial.println(secsSince1900); 1090 1091 // now convert NTP time into everyday time: 1092 Serial.print(F("Unix time = ")); 1093 // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: 1094 const unsigned long seventyYears = 2208988800UL; 1095 // subtract seventy years: 1096 unsigned long epoch = secsSince1900 - seventyYears + (timezone * SECS_PER_HOUR); 1097 // print Unix time: 1098 Serial.println(epoch); 1099 setTime((time_t)epoch); // update the clock 1100 t.year = year(); // record the last NTP time set 1101 t.mon = month() ; 1102 t.mday = day(); 1103 t.hour = hour(); 1104 t.min = minute(); 1105 t.sec = second(); 1106 1107 // print the hour, minute and second: 1108 Serial.print(F("In time zone ")); 1109 Serial.print(timezone); 1110 Serial.print(F(" the time is ")); 1111 Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) 1112 Serial.print(':'); 1113 if ( ((epoch % 3600) / 60) < 10 ) { 1114 // In the first 10 minutes of each hour, we'll want a leading '0' 1115 Serial.print('0'); 1116 } 1117 Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) 1118 Serial.print(':'); 1119 if ( (epoch % 60) < 10 ) { 1120 // In the first 10 seconds of each minute, we'll want a leading '0' 1121 Serial.print('0'); 1122 } 1123 Serial.println(epoch % 60); // print the second 1124} 1125 1126void sendTDTREnd(){ 1127 client.println(F("</td></tr>")); 1128} 1129 1130void processTCPClient(EthernetClient Target){ 1131 boolean currentLineIsBlank = true; 1132 long j , i = 0 ; 1133 while (Target.connected()) { 1134 if (Target.available()) { 1135 String req = Target.readStringUntil('\ '); 1136 // so you can send a reply 1137 // send a standard http response header 1138 i = req.indexOf("?tname="); // wanta do this one BEFORE we dick with captialisation of the request 1139 if (i != -1){ // have a request to set the time zone 1140 j = req.indexOf("&"); 1141 req.substring(i+7,j).toCharArray(trackername , 16) ; 1142 for (j=0 ; j < 16 ; j++){ 1143 if (trackername[j] == '+' ) { 1144 trackername[j] = ' ' ; 1145 } 1146 } 1147 } 1148 1149 i = req.indexOf("/gpio/"); 1150// Serial.println(req); 1151// Serial.println(req.charAt(i+6)); 1152 if (i != -1){ 1153 switch(req.charAt(i+6)){ 1154 case '0': 1155 break; 1156 case '1': 1157 break; 1158 case '2': 1159 break; 1160 case '3': 1161 break; 1162 case '4': 1163 break; 1164 case '5': 1165 break; 1166 case '6': 1167 break; 1168 case '7': 1169 break; 1170 case 'A': 1171 break; 1172 case 'B': 1173 break; 1174 case 'C': 1175 break; 1176 case 'T': // TssmmhhWDDMMYYYY aka set time same format as example eg T001513624032017 1177 t.sec = req.substring(i+7,i+9).toInt(); // 1 1178 t.min = req.substring(i+9,i+11).toInt(); // 3 1179 t.hour = req.substring(i+11,i+13).toInt(); // 5 1180 t.wday = req.charAt(i+13) - 48; // 7 1181 t.mday = req.substring(i+14,i+16).toInt(); // 8 1182 t.mon = req.substring(i+16,i+18).toInt();; // 10 1183 t.year = req.substring(i+18).toInt(); // 12 1184// snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 1185// Serial.println(buff) ; 1186 DS3231_set(t); // set external RTC 1187 setTime((int)t.hour,(int)t.min,(int)t.sec,(int)t.mday,(int)t.mon,(int)t.year ) ; // set the internal RTC 1188 1189 break; 1190 case 't': // force NTP packet 1191 sendNTPpacket(timeServer); // send an NTP packet if requested to 1192 break; 1193 case 'S': // save all the params 1194 iDoSave = 2 ; 1195 break; 1196 case 'R': // read params back from eeprom 1197 iDoSave = 3 ; 1198 break; 1199 case 'Y': 1200 break; 1201 case 'X': 1202 break; 1203 default: 1204 break; 1205 } 1206 }else{ 1207 req.toLowerCase(); 1208 i = req.indexOf(".csv"); 1209 if (i != -1){ // have a request get a CSV filefrom the SD card 1210 i = req.indexOf(".csv"); 1211 Target.flush(); 1212 Target.println(F("HTTP/1.1 200 OK")); 1213 Target.println(F("Content-Type: text/csv")); 1214 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1215 Target.println(""); // this blank line be important 1216 SendFile(req.substring(4,i+4),Target); 1217 break; 1218 }else{ 1219 i = req.indexOf(".jpg"); 1220 if (i != -1){ // have a request get a CSV filefrom the SD card 1221 i = req.indexOf(".jpg"); 1222 Serial.println(req); 1223 Serial.println(req.substring(4,i+4)); 1224 Target.flush(); 1225 Target.println(F("HTTP/1.1 200 OK")); 1226 Target.println(F("Content-Type: image/jpeg")); 1227 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1228 Target.println(""); // this blank line be important 1229 SendFile(req.substring(4,i+4),Target); 1230 break; 1231 }else{ 1232 i = req.indexOf("index.htm"); 1233 if (i != -1){ // have a request get a CSV filefrom the SD card 1234 Target.flush(); 1235 Target.println(F("HTTP/1.1 200 OK")); 1236 Target.println(F("Content-Type: text/html")); 1237 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1238 Target.println(""); // this blank line be important 1239 Target.println(F("<!DOCTYPE HTML>")); 1240 Target.println(F("<head><title>Team Trouble SD Card</title>")); 1241 Target.println(F("<meta name=viewport content='width=320, auto inital-scale=1'>")); 1242 Target.print(F("</head><body><html><center><b><h2>Team Trouble SD Card</h2></b><br><pre>")); 1243 File root = SD.open("/"); 1244 printDirectory(root,0,Target,"/"); 1245 root.close(); 1246 Target.println("</pre><br><br><a href=''>Home Page</a></body></html>"); 1247 break; 1248 }else{ 1249 i = req.indexOf("stats.htm"); 1250 if (i != -1){ // have a request get a status.htm 1251 Target.flush(); 1252 Target.println(F("HTTP/1.1 200 OK")); 1253 Target.println(F("Content-Type: text/html")); 1254 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1255 Target.println(""); // this blank line be important 1256 Target.println(F("<!DOCTYPE HTML>")); 1257 Target.println(F("<head><title>Stats Page</title>")); 1258 Target.println(F("<meta name=viewport content='width=320, auto inital-scale=1'>")); 1259 Target.print(F("</head><body><html><center><b><h2>Stats Page</h2></b>")); 1260 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 1261 Target.println(buff) ; 1262 Target.println(F("</table><br><br><a href=''>Home Page</a></body></html>")); 1263 break; 1264 } 1265 } 1266 } 1267 } 1268 } 1269 1270 i = req.indexOf("?tmode="); 1271 if (i != -1){ // have a request to set the tracking mode 1272 iTrackMode = req.substring(i+7).toInt() ; 1273 if (( iTrackMode < -1) || ( iTrackMode > 4 )){ 1274 iTrackMode = -1 ; 1275 } 1276 } 1277 i = req.indexOf("?tzone="); 1278 if (i != -1){ // have a request to set the time zone 1279 timezone = req.substring(i+7).toInt() ; 1280 if (( timezone < -23) || ( timezone > 23 )){ 1281 timezone = 10 ; 1282 } 1283 } 1284 i = req.indexOf("?mylat="); //lat 1285 if (i != -1){ // have a request to set the latitude 1286 latitude = req.substring(i+7).toFloat() ; 1287 if (( latitude < -90) || ( latitude > 90 )){ 1288 latitude = -34.051219 ; 1289 } 1290 } 1291 i = req.indexOf("?mylon="); // long 1292 if (i != -1){ // have a request to set the logitude 1293 longitude = req.substring(i+7).toFloat() ; 1294 if (( longitude < -180) || ( longitude > 180 )){ 1295 longitude = 142.013618 ; 1296 } 1297 } 1298 i = req.indexOf("?minay="); 1299 if (i != -1){ // have a request to set minimum angle Y 1300 yMinVal = req.substring(i+7).toFloat() ; 1301 if (( yMinVal < -70) || ( yMinVal > 50 )){ 1302 yMinVal = -65 ; // set to default 1303 } 1304 } 1305 i = req.indexOf("?minax="); 1306 if (i != -1){ // have a request to set minimum angle X 1307 xMinVal = req.substring(i+7).toFloat() ; 1308 if (( xMinVal < -10) || ( xMinVal > 60 )){ 1309 xMinVal = 0 ; // set to default 1310 } 1311 } 1312 i = req.indexOf("?maxay="); 1313 if (i != -1){ // have a request to set maximum angle Y 1314 yMaxVal = req.substring(i+7).toFloat() ; 1315 if (( yMaxVal < -70) || ( yMaxVal > 50 )){ 1316 yMaxVal = 45 ; // set to default 1317 } 1318 } 1319 i = req.indexOf("?maxax="); 1320 if (i != -1){ // have a request to set maximum angle X 1321 xMaxVal = req.substring(i+7).toFloat() ; 1322 if (( xMaxVal < -10) || ( xMaxVal > 60 )){ 1323 xMaxVal = 50 ; // set to default 1324 } 1325 } 1326 i = req.indexOf("?paray="); 1327 if (i != -1){ // have a request to set park angle Y 1328 dyPark = req.substring(i+7).toFloat() ; 1329 if (( dyPark < -70) || ( dyPark > 50 )){ 1330 dyPark = 0 ; // set to default 1331 } 1332 } 1333 i = req.indexOf("?parax="); 1334 if (i != -1){ // have a request to set park angle X 1335 dxPark = req.substring(i+7).toFloat() ; 1336 if (( dxPark < -10) || ( dxPark > 60 )){ 1337 dxPark = 6 ; // set to default 1338 } 1339 } 1340 i = req.indexOf("?offay="); 1341 if (i != -1){ // have a request to set offset angle Y 1342 yzOffset = req.substring(i+7).toFloat() ; 1343 if (( yzOffset < -20) || ( yzOffset > 20 )){ 1344 yzOffset = 0 ; // set to default 1345 } 1346 } 1347 i = req.indexOf("?offax="); 1348 if (i != -1){ // have a request to set offset angle X 1349 xzOffset = req.substring(i+7).toFloat() ; 1350 if (( xzOffset < -20) || ( xzOffset > 20 )){ 1351 xzOffset = 0 ; // set to default 1352 } 1353 } 1354 i = req.indexOf("?hysay="); 1355 if (i != -1){ // have a request to set Hysterisis angle 1356 yzH = req.substring(i+7).toFloat() ; 1357 if (( yzH < -20) || ( yzH > 20 )){ 1358 yzH = 4 ; // set to default 1359 } 1360 } 1361 i = req.indexOf("?hysax="); 1362 if (i != -1){ // have a request to set Hysterisis angle 1363 xzH = req.substring(i+7).toFloat() ; 1364 if (( xzH < -20) || ( xzH > 20 )){ 1365 xzH = 4 ; // set to default 1366 } 1367 } 1368 i = req.indexOf("?taray="); 1369 if (i != -1){ // have a request to set Hysterisis angle 1370 yzTarget = req.substring(i+7).toFloat() ; 1371 if (( yzTarget < -70) || ( yzTarget > 50 )){ 1372 yzTarget = 0 ; // set to default 1373 } 1374 } 1375 i = req.indexOf("?tarax="); 1376 if (i != -1){ // have a request to set Hysterisis angle 1377 xzTarget = req.substring(i+7).toFloat() ; 1378 if (( xzTarget < -10) || ( xzTarget > 60 )){ 1379 xzTarget = 0 ; // set to default 1380 } 1381 } 1382 1383 i = req.indexOf("?mulax="); 1384 if (i != -1){ // have a request to set Axis Multiplier 1385 xMul = req.substring(i+7).toFloat() ; 1386 if (( xMul < -10) || ( xMul > 10 )){ 1387 xMul = 1.0 ; // set to default 1388 } 1389 } 1390 i = req.indexOf("?mulay="); 1391 if (i != -1){ // have a request to set Axis Multiplier 1392 yMul = req.substring(i+7).toFloat() ; 1393 if (( yMul < -10) || ( yMul > 10 )){ 1394 yMul = 1.0 ; // set to default 1395 } 1396 } 1397 i = req.indexOf("?mulaz="); 1398 if (i != -1){ // have a request to set Axis Multiplier 1399 zMul = req.substring(i+7).toFloat() ; 1400 if (( zMul < -10) || ( zMul > 10 )){ 1401 zMul = 1.0 ; // set to default 1402 } 1403 } 1404 i = req.indexOf("?xyswp="); 1405 if (i != -1){ // have a request to set the time zone 1406 iXYS = req.substring(i+7).toInt() ; 1407 if (( iXYS < 0) || ( iXYS > 1 )){ 1408 iXYS = 0 ; 1409 } 1410 } 1411 i = req.indexOf("?nisht="); 1412 if (i != -1){ // have a request to set the time zone 1413 iNightShutdown = req.substring(i+7).toInt() ; 1414 if (( iNightShutdown < 0) || ( iNightShutdown > 1 )){ 1415 iNightShutdown = 0 ; 1416 } 1417 } 1418 1419 1420 1421 Target.flush(); 1422 Target.println(F("HTTP/1.1 200 OK")); 1423 Target.println(F("Content-Type: text/html")); 1424 Target.println(F("Connection: close")); // the connection will be closed after completion of the response 1425 Target.println(""); // this blank line be important 1426 Target.println(F("<!DOCTYPE HTML>")); 1427 Target.println(F("<head><title>Team Trouble - Solar Tracker</title>")); 1428 Target.println(F("<meta name=viewport content='width=320, auto inital-scale=1'>")); 1429 1430 Target.print(F("</head><body><html><center><h2>")); 1431 Target.print(String(trackername).substring(0,16)); 1432 Target.print(F(" Solar Tracker</h2>")); 1433 1434 if (hasRTC){ 1435 DS3231_get(&td); 1436 } 1437 Target.println(F("<b>Clocks</b>")) ; 1438 Target.println(F("<table border=1 title='Clocks'>")); 1439 Target.println(F("<tr><th>Clock Source</th><th>Time</th></tr>")); 1440 Target.print(F("<tr><td>Dallas RTC</td><td>")); 1441 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d<br>", td.year, td.mon, td.mday , tc.hour, tc.min, tc.sec); 1442 Target.print(buff) ; 1443 sendTDTREnd(); 1444 Target.println(F("</td></tr>")); 1445 Target.print(F("<tr><td>Last NTP</td><td>")); 1446 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d<br>", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 1447 Target.print(buff) ; 1448 sendTDTREnd(); 1449 Target.print(F("<tr><td>Last GPS</td><td>")); 1450 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d<br>", tg.year, tg.mon, tg.mday , tg.hour, tg.min, tg.sec); 1451 Target.print(buff) ; 1452 sendTDTREnd(); 1453 Target.print(F("<tr><td><b>Arduino Time</b></td><td><b>")); 1454 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", year(), month(), day() , hour(), minute(), second()); 1455 Target.print(buff) ; 1456 sendTDTREnd(); 1457 Target.println(F("</table>")); 1458 1459 Target.println(F("<br><b>Tracker Control System</b>")) ; 1460 1461 Target.println(F("<table border=1 title='Tracker Control'>")); 1462 Target.println(F("<tr><th> Parameter</th><th>Value</th></tr>")); 1463// Target.println("<tr><td>Tracking Mode</td><td align=center>" + String(iTrackMode) + "</td></tr>" ) ; 1464 1465 Target.print(F("<form method=get action=/><tr><td>Tracking Mode</td><td align=center>")) ; 1466 Target.print(F("<select name='tmode'>")); 1467 for (i = -1 ; i < 5 ; i++ ){ 1468 Target.print(F("<option value='")); 1469 Target.print(i) ; 1470 if ( iTrackMode == i ){ 1471 Target.print(F("' SELECTED>")); 1472 }else{ 1473 Target.print(F("'>")); 1474 } 1475 Target.print(i); 1476 Target.print(" "); 1477 switch (i){ 1478 case -1: 1479 Target.print(F("Track Both Park Both")); 1480 break; 1481 case 0: 1482 Target.print(F("Track Both Park Only E/W")); 1483 break; 1484 case 1: 1485 Target.print(F("Track and Park E/W Only")); 1486 break; 1487 case 2: 1488 Target.print(F("Track and Park N/S Only")); 1489 break; 1490 case 3: 1491 Target.print(F("Dont Track Dont Park")); 1492 break; 1493 case 4: 1494 Target.print(F("Dont Track Park Both")); 1495 break; 1496 } 1497 } 1498 Target.print(F("</select>")) ; 1499 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1500 1501 Target.print(F("<form method=get action=/><tr><td>Time Zone</td><td align=center>")) ; 1502 Target.print(F("<input type='text' name='tzone' value='")); 1503 Target.print(timezone); 1504 Target.print(F("' size=6 maxlength=2>")) ; 1505 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1506 1507 Target.print(F("<form method=get action=/><tr><td>Latitude +N -S</td><td align=center>")) ; 1508 Target.print(F("<input type='text' name='mylat' value='")); 1509 Target.print(latitude,8); 1510 Target.print(F("' size=12>")) ; 1511 Target.println(F("</td><td><input type='submit' value='SET'></td></tr></form>")); 1512 1513 Target.print(F("<form method=get action=/><tr><td>Longitude</td><td align=center>")) ; 1514 Target.