Components and supplies
Arduino UNO
Real Time clock Modual
P82B96 Data Buffer
GYRO / MAG / ACC / TEMP / PRES sensor
LED matrix display
Satellite Jacks - Actuator to move the array 1 x 36" HD 1 x 24" STD
Solar Regulator- Powers motors and Arduino Board
12 to 30V step up power supply
Automotive fuse holders
2004 LCD with I2C backpack
Rotary Encoder with Push-Button
Battery 12V 7 AH - The ubiquitous UPS battery
HC-05 Bluetooth Module
Tools and machines
Fabrication Equipment - Welder / Cutters / Drill
Veroboard
Machining - Lathe is needed to machine shafts to bearings
Hand Drill
Apps and platforms
Arduino IDE
Android Studio
Visual Studio 2015
Project description
Code
Modified I2C library
c_cpp
Been asked a few times for my tweaked library files.. Small changes to suit LCD's and boards in my "kit" Thanks to the original author... From this you should find the rest...
1// --------------------------------------------------------------------------- 2// Created by Francisco Malpartida on 20/08/11. 3// Copyright 2011 - Under creative commons license 3.0: 4// Attribution-ShareAlike CC BY-SA 5// 6// This software is furnished "as is", without technical support, and with no 7// warranty, express or implied, as to its usefulness for any purpose. 8// 9// Thread Safe: No 10// Extendable: Yes 11// 12// @file LiquidCrystal_I2C.c 13// This file implements a basic liquid crystal library that comes as standard 14// in the Arduino SDK but using an I2C IO extension board. 15// 16// @brief 17// This is a basic implementation of the LiquidCrystal library of the 18// Arduino SDK. The original library has been reworked in such a way that 19// this class implements the all methods to command an LCD based 20// on the Hitachi HD44780 and compatible chipsets using I2C extension 21// backpacks such as the I2CLCDextraIO with the PCF8574* I2C IO Expander ASIC. 22// 23// The functionality provided by this class and its base class is identical 24// to the original functionality of the Arduino LiquidCrystal library. 25// 26// 27// 28// @author F. Malpartida - fmalpartida@gmail.com 29// --------------------------------------------------------------------------- 30#if (ARDUINO < 100) 31#include <WProgram.h> 32#else 33#include <Arduino.h> 34#endif 35#include <inttypes.h> 36#include "I2CIO.h" 37#include "LiquidCrystal_I2C.h" 38 39// CONSTANT definitions 40// --------------------------------------------------------------------------- 41 42// flags for backlight control 43/*! 44 @defined 45 @abstract LCD_NOBACKLIGHT 46 @discussion NO BACKLIGHT MASK 47 */ 48#define LCD_NOBACKLIGHT 0x00 49 50/*! 51 @defined 52 @abstract LCD_BACKLIGHT 53 @discussion BACKLIGHT MASK used when backlight is on 54 */ 55#define LCD_BACKLIGHT 0xFF 56 57 58// Default library configuration parameters used by class constructor with 59// only the I2C address field. 60// --------------------------------------------------------------------------- 61/*! 62 @defined 63 @abstract Enable bit of the LCD 64 @discussion Defines the IO of the expander connected to the LCD Enable 65 */ 66#define EN 2 // Enable bit 67 68/*! 69 @defined 70 @abstract Read/Write bit of the LCD 71 @discussion Defines the IO of the expander connected to the LCD Rw pin 72 */ 73#define RW 1 // Read/Write bit 74 75/*! 76 @defined 77 @abstract Register bit of the LCD 78 @discussion Defines the IO of the expander connected to the LCD Register select pin 79 */ 80#define RS 0 // Register select bit 81 82/*! 83 @defined 84 @abstract LCD dataline allocation this library only supports 4 bit LCD control 85 mode. 86 @discussion D4, D5, D6, D7 LCD data lines pin mapping of the extender module 87 */ 88#define D4 4 89#define D5 5 90#define D6 6 91#define D7 7 92 93 94// CONSTRUCTORS 95// --------------------------------------------------------------------------- 96LiquidCrystal_I2C::LiquidCrystal_I2C( uint8_t lcd_Addr ) 97{ 98 config(lcd_Addr, EN, RW, RS, D4, D5, D6, D7); 99} 100 101 102LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t backlighPin, 103 t_backlighPol pol = POSITIVE) 104{ 105 config(lcd_Addr, EN, RW, RS, D4, D5, D6, D7); 106 setBacklightPin(backlighPin, pol); 107} 108 109LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, 110 uint8_t Rs) 111{ 112 config(lcd_Addr, En, Rw, Rs, D4, D5, D6, D7); 113} 114 115LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, 116 uint8_t Rs, uint8_t backlighPin, 117 t_backlighPol pol = POSITIVE) 118{ 119 config(lcd_Addr, En, Rw, Rs, D4, D5, D6, D7); 120 setBacklightPin(backlighPin, pol); 121} 122 123LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, 124 uint8_t Rs, uint8_t d4, uint8_t d5, 125 uint8_t d6, uint8_t d7 ) 126{ 127 config(lcd_Addr, En, Rw, Rs, d4, d5, d6, d7); 128} 129 130LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t En, uint8_t Rw, 131 uint8_t Rs, uint8_t d4, uint8_t d5, 132 uint8_t d6, uint8_t d7, uint8_t backlighPin, 133 t_backlighPol pol = POSITIVE ) 134{ 135 config(lcd_Addr, En, Rw, Rs, d4, d5, d6, d7); 136 setBacklightPin(backlighPin, pol); 137} 138 139// PUBLIC METHODS 140// --------------------------------------------------------------------------- 141 142// 143// begin 144void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) 145{ 146 147 init(); // Initialise the I2C expander interface 148 LCD::begin ( cols, lines, dotsize ); 149} 150 151 152// User commands - users can expand this section 153//---------------------------------------------------------------------------- 154// Turn the (optional) backlight off/on 155 156// 157// setBacklightPin 158void LiquidCrystal_I2C::setBacklightPin ( uint8_t value, t_backlighPol pol = POSITIVE ) 159{ 160 _backlightPinMask = ( 1 << value ); 161 _polarity = pol; 162 setBacklight(BACKLIGHT_OFF); 163} 164 165// 166// setBacklight 167void LiquidCrystal_I2C::setBacklight( uint8_t value ) 168{ 169 // Check if backlight is available 170 // ---------------------------------------------------- 171 if ( _backlightPinMask != 0x0 ) 172 { 173 // Check for polarity to configure mask accordingly 174 // ---------------------------------------------------------- 175 if (((_polarity == POSITIVE) && (value > 0)) || 176 ((_polarity == NEGATIVE ) && ( value == 0 ))) 177 { 178 _backlightStsMask = _backlightPinMask & LCD_BACKLIGHT; 179 } 180 else 181 { 182 _backlightStsMask = _backlightPinMask & LCD_NOBACKLIGHT; 183 } 184 _i2cio.write( _backlightStsMask ); 185 } 186} 187 188 189// PRIVATE METHODS 190// --------------------------------------------------------------------------- 191 192// 193// init 194int LiquidCrystal_I2C::init() 195{ 196 int status = 0; 197 198 // initialize the backpack IO expander 199 // and display functions. 200 // ------------------------------------------------------------------------ 201 if ( _i2cio.begin ( _Addr ) == 1 ) 202 { 203 _i2cio.portMode ( OUTPUT ); // Set the entire IO extender to OUTPUT 204 _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; 205 status = 1; 206 _i2cio.write(0); // Set the entire port to LOW 207 } 208 return ( status ); 209} 210 211// 212// config 213void LiquidCrystal_I2C::config (uint8_t lcd_Addr, uint8_t En, uint8_t Rw, uint8_t Rs, 214 uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7 ) 215{ 216 _Addr = lcd_Addr; 217 218 _backlightPinMask = 0x08; 219 _backlightStsMask = LCD_BACKLIGHT; 220 _polarity = POSITIVE; 221 222 _En = ( 1 << En ); 223 _Rw = ( 1 << Rw ); 224 _Rs = ( 1 << Rs ); 225 226 // Initialise pin mapping 227 _data_pins[0] = ( 1 << d4 ); 228 _data_pins[1] = ( 1 << d5 ); 229 _data_pins[2] = ( 1 << d6 ); 230 _data_pins[3] = ( 1 << d7 ); 231} 232 233 234 235// low level data pushing commands 236//---------------------------------------------------------------------------- 237 238// 239// send - write either command or data 240void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) 241{ 242 // No need to use the delay routines since the time taken to write takes 243 // longer that what is needed both for toggling and enable pin an to execute 244 // the command. 245 246 if ( mode == FOUR_BITS ) 247 { 248 write4bits( (value & 0x0F), COMMAND ); 249 } 250 else 251 { 252 write4bits( (value >> 4), mode ); 253 write4bits( (value & 0x0F), mode); 254 } 255 delay(1); 256} 257 258// 259// write4bits 260void LiquidCrystal_I2C::write4bits ( uint8_t value, uint8_t mode ) 261{ 262 uint8_t pinMapValue = 0; 263 264 // Map the value to LCD pin mapping 265 // -------------------------------- 266 for ( uint8_t i = 0; i < 4; i++ ) 267 { 268 if ( ( value & 0x1 ) == 1 ) 269 { 270 pinMapValue |= _data_pins[i]; 271 } 272 value = ( value >> 1 ); 273 } 274 275 // Is it a command or data 276 // ----------------------- 277 if ( mode == DATA ) 278 { 279 mode = _Rs; 280 } 281 282 pinMapValue |= mode | _backlightStsMask; 283 pulseEnable ( pinMapValue ); 284} 285 286// 287// pulseEnable 288void LiquidCrystal_I2C::pulseEnable (uint8_t data) 289{ 290 _i2cio.write (data | _En); // En HIGH 291 _i2cio.write (data & ~_En); // En LOW 292}
TRACKER_ALL_IN_ONE_BOTTOM_PWM_SOFT.ino
c_cpp
PWM soft starter variation on the original design ... wiring changes described in top of the file
1//#include <SFE_BMP180.h> 2#include <avr/wdt.h> 3#include <Wire.h> 4#include <LSM303.h> // modified ... fixed a couple of bugs 5#include <LiquidCrystal_I2C.h> 6//#include <L3G.h> 7#include "ds3231.h" 8#include "ht16k33.h" 9#include <ModbusRtu.h> // Modified ... this no longer a stock lib - Slave supports register translation/mapping 10#include <EEPROM.h> 11#include <math.h> 12 13#define ID 1 14 15#define BUFF_MAX 32 16#define PARK_EAST 1 17#define PARK_WEST 2 18#define PARK_NORTH 3 19#define PARK_SOUTH 4 20#define PARK_FLAT 5 21 22#define MOTOR_DWELL 100 23 24#define MAX_MODBUS_DATA 60 25 26#define HT16K33_DSP_NOBLINK 0 // constants for the half arsed cheapo display 27#define HT16K33_DSP_BLINK1HZ 4 28#define HT16K33_DSP_BLINK2HZ 2 29#define HT16K33_DSP_BLINK05HZ 6 30 31 32const byte ENCODER_PINA = 2; // encoder connects - interupt pin 33const byte ENCODER_PINB = 3; // non interupt pin 34const byte ENCODER_PB = 8; 35 36const byte RELAY_XZ_DIR = 4; // DIR 1 X+ X- North / South Was the X+ N relay 37const byte RELAY_XZ_PWM = 5; // PWM 1 Speed North / South Was the X- S relay 38const byte RELAY_YZ_PWM = 6; // PWM 2 Speed East / West Was the Y+ W relay 39const byte RELAY_YZ_DIR = 7; // DIR 2 Y+ Y- East / West Was the Y- E relay 40 41const byte UNUSED09 = 9; // cycle timer 26 Hz 38ms period 42const byte UNUSED10 = 10; 43const byte UNUSED11 = 11; 44const byte WATCHDOG = 12; 45 46 47//L3G gyro; 48LSM303 compass; 49LiquidCrystal_I2C lcd(0x27); // Set the LCD I2C address I modified the lib for default wiring 50 51//SFE_BMP180 pressure; 52HT16K33 HT; 53 54Modbus slave1(ID, 1, 0); // this is slave ID and RS-232 or USB-FTDI 55 56uint8_t time[8]; 57int motor_recycle = 0 ; 58char recv[BUFF_MAX]; 59unsigned int recv_size = 0; 60unsigned long prev_millis; 61uint8_t u8state; //!< machine state 62uint8_t u8query; //!< pointer to message query 63 64struct ts t; // MODBUS MAP 65struct ts tc; // 44 66int iSave = 0 ; // 43 67int iDoSave = 0 ; // 42 68float T; // 40 temperature of board 69float xzTarget ; // 38 target for angles 70float yzTarget ; // 36 71float xzH ; // 34 hyserisis zone 72float yzH ; // 32 73float xzAng; // 30 current angles 74float yzAng; // 28 75float xzOffset; // 26 offset xz 76float yzOffset; // 24 offset yz 77float dyPark; // 22 parking position 78float dxPark; // 20 79float xMinVal ; // 18 Min and Max values X - N/S 80float xMaxVal ; // 16 81float yMinVal ; // 14 Y -- E/W 82float yMaxVal ; // 12 83float latitude; // 10 84float longitude; // 8 85int timezone; // 7 86int iDayNight ; // 6 87float solar_az_deg; // 4 88float solar_el_deg; // 2 89int iTrackMode ; // 1 90int iMode ; // 0 91 92int iCycle ; 93int iPMode; 94int iPWM_YZ ; 95int iPWM_XZ ; 96 97unsigned long tempus; 98int8_t state1 = 0; 99 100void StopYZ(){ 101 iPWM_YZ=0 ; 102 motor_recycle = MOTOR_DWELL ; 103} 104void StopXZ(){ 105 iPWM_XZ=0 ; 106 motor_recycle = MOTOR_DWELL ; 107} 108 109void ActivateRelays(int iAllStop) { 110 if (motor_recycle > 0 ){ 111 motor_recycle-- ; 112 } 113 if ( iAllStop == 0 ) { 114 StopYZ() ; 115 StopXZ() ; 116 } else { 117 if (( iPWM_YZ==0 ) && (motor_recycle == 0 )){ 118 if (((yzAng ) < ( yzTarget - yzH )) ) { // do Y ie E/W before N/S 119 digitalWrite(RELAY_YZ_DIR, LOW) ; 120 iPWM_YZ=128 ; 121 } 122 if (((yzAng ) > (yzTarget + yzH )) ) { 123 digitalWrite(RELAY_YZ_DIR, HIGH) ; 124 iPWM_YZ=128 ; 125 } 126 } 127 if ( iPWM_YZ>0 ){ 128 if ((yzAng > yzTarget) && ( digitalRead(RELAY_YZ_DIR)==LOW )) { 129 StopYZ() ; 130 } 131 if ((yzAng < yzTarget) && ( digitalRead(RELAY_YZ_DIR)==HIGH )) { 132 StopYZ() ; 133 } 134 } 135 136 if (( iPWM_YZ==0)) { // if finished on E/W you can do N/S 137 if (( iPWM_XZ==0 ) && (motor_recycle == 0 )){ 138 if ((xzAng < ( xzTarget - xzH )) ) { // turn on if not in tolerance 139 digitalWrite(RELAY_XZ_DIR, LOW) ; 140 iPWM_XZ=128 ; 141 } 142 if ((xzAng > ( xzTarget + xzH )) ) { // turn on if not in tolerance 143 digitalWrite(RELAY_XZ_DIR, HIGH) ; 144 iPWM_XZ=128 ; 145 } 146 } 147 }else{ 148 if ((iPWM_XZ>0 )){ 149 StopXZ() ; 150 } 151 } 152 if ( iPWM_XZ>0 ){ 153 if ((xzAng > xzTarget ) && ( digitalRead(RELAY_XZ_DIR)==LOW )) { // if on turn off 154 StopXZ() ; 155 } 156 if ((xzAng < xzTarget ) && ( digitalRead(RELAY_XZ_DIR)==HIGH )) { // if on turn off 157 StopXZ() ; 158 } 159 } 160 } 161 if (iPWM_XZ>0){ 162 iPWM_XZ += 3 ; 163 } 164 if (iPWM_YZ>0){ 165 iPWM_YZ += 3 ; 166 } 167 iPWM_XZ = constrain(iPWM_XZ,0,254); 168 iPWM_YZ = constrain(iPWM_YZ,0,254); 169 analogWrite(RELAY_XZ_PWM,iPWM_XZ); 170 analogWrite(RELAY_YZ_PWM,iPWM_YZ); 171} 172 173void FloatToModbusWords(float src_value , uint16_t * dest_lo , uint16_t * dest_hi ) { 174 uint16_t tempdata[2] ; 175 float *tf ; 176 tf = (float * )&tempdata[0] ; 177 *tf = src_value ; 178 *dest_lo = tempdata[1] ; 179 *dest_hi = tempdata[0] ; 180} 181float FloatFromModbusWords( uint16_t dest_lo , uint16_t dest_hi ) { 182 uint16_t tempdata[2] ; 183 float *tf ; 184 tf = (float * )&tempdata[0] ; 185 tempdata[1] = dest_lo ; 186 tempdata[0] = dest_hi ; 187 return (*tf) ; 188} 189 190// Arduino doesnt have these to we define from a sandard libruary 191float arcsin(float x) { 192 return (atan(x / sqrt(-x * x + 1))); 193} 194float arccos(float x) { 195 return (atan(x / sqrt(-x * x + 1)) + (2 * atan(1))); 196} 197// fractional orbital rotation in radians 198float gama(struct ts *tm) { 199 return ((2 * PI / 365 ) * DayOfYear(tm->year , tm->mon , tm->mday , tm->hour , tm->min )); 200} 201// equation of rime 202float eqTime(float g) { 203 return (229.18 * ( 0.000075 + ( 0.001868 * cos(g)) - (0.032077 * sin(g)) - (0.014615 * cos (2 * g)) - (0.040849 * sin(2 * g)))); 204} 205// declination of sun in radians 206float Decl(float g) { 207 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)) ); 208} 209float TimeOffset(float longitude , struct ts *tm , int timezone ) { 210 float dTmp ; 211 dTmp = (-4.0 * longitude ) + (60 * timezone) - eqTime(gama(tm)) ; 212 return (dTmp); 213} 214 215float TrueSolarTime(float longitude , struct ts *tm , int timezone ) { 216 float dTmp ; 217 dTmp = ( 60.0 * tm->hour ) + (1.0 * tm->min) + (1.0 * tm->sec / 60) - TimeOffset(longitude, tm, timezone) ; 218 return (dTmp); 219} 220float HourAngle(float longitude , struct ts *tm , int timezone) { 221 float dTmp; 222 dTmp = (TrueSolarTime(longitude, tm, timezone) / 4 ) - 180 ; // 720 minutes is solar noon -- div 4 is 180 223 return (dTmp); 224} 225// Hour angle for sunrise and sunset only 226float HA (float lat , struct ts *tm ) { 227 float latRad ; 228 latRad = lat * 2 * PI / 360 ; 229 return ( acos((cos(90.833 * PI / 180 ) / ( cos(latRad) * cos(Decl(gama(tm)))) - (tan(latRad) * tan(Decl(gama(tm)))))) / PI * 180 ); 230} 231 232float Sunrise(float longitude , float lat , struct ts *tm , int timezone) { 233 return (720 - ( 4.0 * (longitude + HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 234} 235float Sunset(float longitude , float lat , struct ts *tm , int timezone) { 236 return (720 - ( 4.0 * (longitude - HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 237} 238float SNoon(float longitude , float lat , struct ts *tm , int timezone) { 239 return (720 - ( 4.0 * (longitude + (60 * timezone) - eqTime(gama(tm)))) ) ; 240} 241 242float SolarZenithRad(float longitude , float lat , struct ts *tm , int timezone) { 243 float latRad ; 244 float decRad ; 245 float HourAngleRad ; 246 float dTmp ; 247 248 latRad = lat * 2 * PI / 360 ; 249 decRad = Decl(gama(tm)); 250 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 251 dTmp = acos((sin(latRad) * sin(decRad)) + (cos(latRad) * cos(decRad) * cos(HourAngleRad))); 252 return (dTmp) ; 253 254} 255float SolarElevationRad(float longitude , float lat , struct ts *tm , int timezone ) { 256 return ((PI / 2) - SolarZenithRad(longitude , lat , tm , timezone )) ; 257} 258 259float SolarAzimouthRad(float longitude , float lat , struct ts *tm , int timezone) { 260 float latRad ; 261 float decRad ; 262 float solarzenRad ; 263 float HourAngleRad ; 264 float dTmp ; 265 latRad = lat * 2 * PI / 360 ; 266 decRad = Decl(gama(tm)); 267 solarzenRad = SolarZenithRad ( longitude , lat , tm , timezone ) ; 268 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 269 dTmp = acos(((sin(decRad) * cos(latRad)) - (cos(HourAngleRad) * cos(decRad) * sin(latRad))) / sin(solarzenRad)) ; 270 if ( HourAngleRad < 0 ) { 271 return (dTmp) ; 272 } else { 273 return ((2 * PI) - dTmp) ; 274 } 275} 276 277float LoadFloatFromEEPROM(int address,float minval,float maxval, float defaultval){ 278float tmp ; 279 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 280 if (( tmp < minval ) || ( tmp > maxval )|| (NumberOK(tmp) == 1)) { 281 tmp = defaultval ; 282 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 283 } 284 return(tmp); 285} 286int LoadIntFromEEPROM(int address,int minval,int maxval, int defaultval){ 287int tmp ; 288 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 289 if (( tmp < minval ) || ( tmp > maxval )) { 290 tmp = defaultval ; 291 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 292 } 293 return(tmp); 294} 295 296int NumberOK (float target) { 297 int tmp = 0 ; 298 tmp = isnan(target); 299 if ( tmp != 1 ) { 300 tmp = isinf(target); 301 } 302 return (tmp); 303} 304 305void IncFloat(float * target, boolean bInc, float increment, float minval, float maxval , boolean bWrap){ 306 if( bInc ){ 307 *target += increment ; 308 }else{ 309 *target -= increment ; 310 } 311 if ( *target > maxval ){ 312 if ( bWrap ){ 313 *target = minval ; 314 }else{ 315 *target = maxval ; 316 } 317 } 318 if ( *target < minval ){ 319 if ( bWrap ){ 320 *target = maxval ; 321 }else{ 322 *target = minval ; 323 } 324 } 325} 326void IncInt(int * target, boolean bInc, int increment, int minval, int maxval , boolean bWrap){ 327 if( bInc ){ 328 *target += increment ; 329 }else{ 330 *target -= increment ; 331 } 332 if ( *target > maxval ){ 333 if ( bWrap ){ 334 *target = minval ; 335 }else{ 336 *target = maxval ; 337 } 338 } 339 if ( *target < minval ){ 340 if ( bWrap ){ 341 *target = maxval ; 342 }else{ 343 *target = minval ; 344 } 345 } 346} 347void IncUint8_t (uint8_t * target , boolean bInc, int increment, int minval, int maxval , boolean bWrap){ 348 if( bInc ){ 349 *target += increment ; 350 }else{ 351 *target -= increment ; 352 } 353 if ( *target > maxval ){ 354 if ( bWrap ){ 355 *target = minval ; 356 }else{ 357 *target = maxval ; 358 } 359 } 360 if ( *target < minval ){ 361 if ( bWrap ){ 362 *target = maxval ; 363 }else{ 364 *target = minval ; 365 } 366 } 367} 368void StdFlloatValueDisplay(float target, char message[]){ 369 lcd.setCursor ( 0, 1 ); // line 1 370 lcd.print(message) ; 371 lcd.print(target) ; 372 lcd.setCursor ( 0, 2 ); // line 2 373 lcd.print( "PB to set ") ; 374} 375 376 377void setup() { 378 int led ; 379 380 Wire.begin(); 381 lcd.begin(20, 4); 382 lcd.home(); 383 lcd.setBacklightPin(3, NEGATIVE); 384 lcd.noCursor(); 385 386 MCUSR &= ~_BV(WDRF); 387 wdt_disable(); 388 389 compass.init(); 390 compass.enableDefault(); 391 compass.setTimeout(1000); 392 393 pinMode(RELAY_XZ_DIR, OUTPUT); // Outputs for PWM motor control 394 pinMode(RELAY_XZ_PWM, OUTPUT); // 395 pinMode(RELAY_YZ_PWM, OUTPUT); // 396 pinMode(RELAY_YZ_DIR, OUTPUT); // 397 iPWM_YZ = 0 ; 398 iPWM_XZ = 0 ; 399 pinMode(13, OUTPUT); // 400 digitalWrite(13, HIGH ); 401 ActivateRelays(0); // call an all stop first 402 403 Serial.begin(9600) ; // use as a diagnostic port 404 slave1.begin( 9600 ); // RS-232 to base of tower 405 tempus = millis() + 100; 406 407 pinMode(ENCODER_PINA, INPUT_PULLUP); 408 pinMode(ENCODER_PINB, INPUT_PULLUP); 409 pinMode(ENCODER_PB, INPUT_PULLUP); 410 attachInterrupt(0, counter, FALLING); 411 412 pinMode(UNUSED09, OUTPUT); // unused so dont leave floating set as output 413 pinMode(UNUSED10, OUTPUT); // 414 pinMode(UNUSED11, OUTPUT); // 415 pinMode(WATCHDOG, OUTPUT); // 416 digitalWrite(WATCHDOG,HIGH); 417 418 compass.m_min = (LSM303::vector<int16_t>) {-3848, -1822, -1551 }; // calibration figures are empirical 419 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300}; 420 421 xzH = LoadFloatFromEEPROM(0,0.1,20.0,4.0); // hysterisis NS 422 yzH = LoadFloatFromEEPROM(1,0.1,20.0,4.0); // "" EW 423 424 dyPark = LoadFloatFromEEPROM(2,-70.0,50.0,0); 425 dxPark = LoadFloatFromEEPROM(3,-5.0,50.0,0.0); 426 427 xzOffset = LoadFloatFromEEPROM(4,-90.0,90.0,0); // NS 428 yzOffset = LoadFloatFromEEPROM(5,-90.0,90.0,0); // EW 429 430 xzTarget = LoadFloatFromEEPROM(6,-90.0,90.0,0); // NS 431 yzTarget = LoadFloatFromEEPROM(7,-90.0,90.0,0); // EW 432 433 xMinVal = LoadFloatFromEEPROM(8,-10.0,60.0,0.0); // NS 434 xMaxVal = LoadFloatFromEEPROM(9,-10.0,60.0,45); 435 436 yMinVal = LoadFloatFromEEPROM(10,-70.0,50.0,-65); // EW 437 yMaxVal = LoadFloatFromEEPROM(11,-70.0,50.0,45); 438 439 iMode = 0 ; 440 iTrackMode = LoadIntFromEEPROM(12,-1,4,0); 441 442// latitude = -34.051219 ; 443// longitude = 142.013618 ; 444// timezone = 10 ; 445 446 latitude = LoadFloatFromEEPROM(13,-90.0,90.0,-34.051219); 447 longitude = LoadFloatFromEEPROM(14,-180.0,180.0,142.013618); 448 timezone = LoadIntFromEEPROM(15,0,23,10); 449 450 DS3231_init(DS3231_INTCN); 451 DS3231_get(&tc); 452 DS3231_get(&t); 453 lcd.clear(); 454 HT.begin(0x00); 455 for (led = 0; led < 127; led++) { 456 HT.clearLedNow(led); 457 } 458 wdt_enable(WDTO_4S); 459} 460 461void loop() { 462 float P; 463 float sunInc; 464 float sunAng; 465 float xzRatio; 466 float yzRatio; 467 char buff[BUFF_MAX]; 468 float decl ; 469 float eqtime ; 470 float dTmp ; 471 float ha ; 472 float heading ; 473 float sunrise ; 474 float sunset ; 475 float tst ; 476 float sunX ; 477 478 compass.read(); // this reads all 6 channels 479// compass.readAcc(); 480// compass.readMag(); 481 digitalWrite(UNUSED09,!digitalRead(UNUSED09)); // toggle this output so I can measure the cycle time with a scope 482 483 T = DS3231_get_treg(); 484 heading = compass.heading((LSM303::vector<int>) { 1, 0, 0 }); 485 486 if (( compass.a.z != 0) && (!compass.timeoutOccurred() )) { 487 xzRatio = (float)compass.a.y / abs((float)compass.a.z) ; // yep if your sharp you can see I swapped axis orientation late in the programming 488 yzRatio = (float)compass.a.x / abs((float)compass.a.z) ; 489 xzAng = ((float)atan(xzRatio) / PI * -180 ) + xzOffset ; // good old offsets or fudge factors 490 yzAng = ((float)atan(yzRatio) / PI * 180 ) + yzOffset ; 491 digitalWrite(13, LOW); 492 }else{ // try restarting the compass/accelerometer modual - cos he gone walkabout... 493 Wire.begin(); // reset the I2C 494 compass.init(); 495 compass.enableDefault(); 496 compass.setTimeout(1000); // BTW I fixed up the int / long issue in the time out function in the LM303 lib I was using 497 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 !!) 498 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300 }; 499 if (tc.sec % 2 == 0 ) { 500 digitalWrite(13, HIGH); 501 } else { 502 digitalWrite(13, LOW); 503 } 504 HT.begin(0x00); 505 } 506 507 DS3231_get(&tc); 508 509 solar_az_deg = SolarAzimouthRad(longitude, latitude, &tc, timezone) * 180 / PI ; 510 solar_el_deg = SolarElevationRad(longitude, latitude, &tc, timezone) * 180 / PI ; 511 512 decl = Decl(gama(&tc)) * 180 / PI ; 513 ha = HourAngle (longitude , &tc , timezone ) ; 514 sunrise = Sunrise(longitude, latitude, &tc, timezone) ; 515 sunset = Sunset(longitude, latitude, &tc, timezone); 516 tst = TrueSolarTime(longitude, &tc, timezone); 517 sunX = abs(latitude) + decl ; 518 if (solar_el_deg >= 0 ){ // day 519 iDayNight = 1 ; 520 }else{ // night 521 iDayNight = 0 ; 522 } 523 524 switch (iTrackMode) { 525 case 4: // both axis to park 526 yzTarget = dyPark ; // night park position E/W 527 xzTarget = dxPark ; // night park position N/S 528 break ; 529 case 3: // both axis off no tracking 530 break ; 531 case 2: // xz tracking NS 532 if ( iDayNight == 1 ) { 533 xzTarget = sunX ; // need to map the coordinate system correctly 534 } else { 535 xzTarget = dxPark ; // night park position 536 } 537 break; 538 case 1: // yz tracking EW 539 if (iDayNight == 1) { 540 yzTarget = ha ; 541 } else { 542 yzTarget = dyPark ; // night park position 543 } 544 break; 545 case -1: // set target to tracking and park both at nigh 546 if (iDayNight == 1) { 547 yzTarget = ha ; 548 xzTarget = sunX ; // need to map the coordinate system correctly 549 } else { 550 yzTarget = dyPark ; // night park position E/W 551 xzTarget = dxPark ; // night park position N/S 552 } 553 break; 554 default: // set target to tracking 555 if (iDayNight == 1) { 556 yzTarget = ha ; 557 xzTarget = sunX ; // need to map the coordinate system correctly 558 } else { 559 yzTarget = dyPark ; // night park position (dont park the other - leave till morning) 560 } 561 break; 562 } 563 xzTarget = constrain(xzTarget,xMinVal,xMaxVal); // constain function... very cool - dont leave home without it ! 564 yzTarget = constrain(yzTarget,yMinVal,yMaxVal); 565 566 if ( iPMode != iMode ) { // clear screen after first change of mode 567 lcd.clear(); 568 if ((( iPMode < 1 ) || ( iPMode > 8 )) && ( iMode > 0 ) && (iMode < 9 )) { 569 DS3231_get(&t); // when going into time edit mode update the time - then leave it allone to be worked on by the editor 570 } 571 iPMode = iMode ; 572 } 573 if ( iCycle != tc.sec ) { //only update once a second 574 digitalWrite(WATCHDOG,LOW); // external watch dog pin 575 wdt_reset(); // reset internal watchdog - good puppy 576 // lcd.clear(); 577 if ( iMode > 0 ){ 578 lcd.setCursor ( 0, 3 ); // line 3 579 lcd.print( "Mode ") ; 580 lcd.print(iMode) ; 581 lcd.print( " " ) ; 582 switch (tc.sec % 2 ){ 583 case 1: 584 lcd.print( "|" ) ; 585 break; 586 default: 587 lcd.print( "-" ) ; 588 break; 589 } 590 } 591 lcd.setCursor ( 0, 0 ); // line 0 592 switch (iMode) { 593 case 22: 594 StdFlloatValueDisplay(yMaxVal,"Y Max "); 595 break; 596 case 21: 597 StdFlloatValueDisplay(yMinVal,"Y Min "); 598 break; 599 case 20: 600 StdFlloatValueDisplay(xMaxVal,"X Max "); 601 break; 602 case 19: 603 StdFlloatValueDisplay(xMinVal,"X Min "); 604 break; 605 case 23: 606 lcd.setCursor ( 0, 0 ); 607 lcd.print("Lat "); 608 lcd.print(latitude,6); 609 lcd.setCursor ( 0, 1 ); 610 lcd.print("Long "); 611 lcd.print(longitude,6); 612 lcd.setCursor ( 0, 2 ); 613 lcd.print("Time Zone "); 614 lcd.print(timezone); 615 break; 616 case 24: 617 // lcd.clear(); 618 lcd.setCursor ( 0, 0 ); 619 lcd.print("X/Z "); 620 if ( xzAng > 0 ) { 621 lcd.print("+"); 622 } 623 lcd.print(xzAng); 624 lcd.setCursor ( 10, 0 ); 625 lcd.print("Y/Z "); 626 if ( yzAng > 0 ) { 627 lcd.print("+"); 628 } 629 lcd.print(yzAng); 630 lcd.setCursor ( 0, 1 ); 631 lcd.print("TX "); 632 if (( xzTarget) > 0 ) { 633 lcd.print("+"); 634 } 635 lcd.print(( xzTarget)); 636 lcd.setCursor ( 10, 1 ); 637 lcd.print("TY "); 638 if (( yzTarget) > 0 ) { 639 lcd.print("+"); 640 } 641 lcd.print(( yzTarget)); 642 lcd.setCursor ( 0, 2 ); 643 lcd.print("DX "); 644 dTmp = ( xzAng - xzTarget) ; 645 if (dTmp > 0 ) { 646 lcd.print("+"); 647 } 648 lcd.print(dTmp); 649 lcd.setCursor ( 10, 2 ); 650 lcd.print("DY "); 651 dTmp = ( yzAng - yzTarget) ; 652 if (dTmp > 0 ) { 653 lcd.print("+"); 654 } 655 lcd.print(dTmp); 656 657 lcd.setCursor ( 10, 3 ); // line 2 658 lcd.print( "T ") ; 659 lcd.print(T) ; 660 661 lcd.setCursor ( 18, 3 ); // line 2 662 if ( iPWM_YZ == 0 ) { 663 lcd.print( " ") ; 664 }else{ 665 if (( digitalRead(RELAY_YZ_DIR) == LOW )) { 666 lcd.print( "W") ; 667 }else{ 668 lcd.print( "E") ; 669 } 670 } 671 lcd.setCursor ( 19, 3 ); // line 2 672 if ( iPWM_XZ == 0 ) { 673 lcd.print( " ") ; 674 }else{ 675 if (( digitalRead(RELAY_XZ_DIR) == LOW )) { 676 lcd.print( "N") ; 677 }else{ 678 lcd.print( "S") ; 679 } 680 } 681 682 break; 683 case 18: // SAVE ALL 684 lcd.setCursor ( 0, 1 ); // line 1 685 if ( iSave != 0 ) { 686 lcd.print( "SAVE REQUIRED") ; 687 } else { 688 lcd.print( "### SAVED ###") ; 689 } 690 if ( iDoSave == 2 ) { 691 EEPROM.put( 0 , xzH ); 692 EEPROM.put(0 + (1 * sizeof(float)) , yzH ); 693 EEPROM.put(0 + (2 * sizeof(float)) , dyPark ); 694 EEPROM.put(0 + (3 * sizeof(float)) , dxPark ); 695 EEPROM.put(0 + (4 * sizeof(float)) , xzOffset ); 696 EEPROM.put(0 + (5 * sizeof(float)) , yzOffset ); 697 EEPROM.put(0 + (6 * sizeof(float)) , xzTarget ); 698 EEPROM.put(0 + (7 * sizeof(float)) , yzTarget ); 699 EEPROM.put(0 + (8 * sizeof(float)) , xMinVal ); 700 EEPROM.put(0 + (9 * sizeof(float)) , xMaxVal ); 701 EEPROM.put(0 + (10 * sizeof(float)) , yMinVal ); 702 EEPROM.put(0 + (11 * sizeof(float)) , yMaxVal ); 703 EEPROM.put(0 + (12 * sizeof(float)) , iTrackMode ); 704 EEPROM.put(0 + (13 * sizeof(float)) , latitude ); 705 EEPROM.put(0 + (14 * sizeof(float)) , longitude ); 706 EEPROM.put(0 + (15 * sizeof(float)) , timezone ); 707 iDoSave = 0 ; 708 iSave = 0 ; 709 } 710 lcd.setCursor ( 0, 2 ); // line 2 711 lcd.print( "PB to Save ALL") ; 712 break ; 713 case 17: // yzH hysterisis 714 StdFlloatValueDisplay(yzH,"Y Hysteresis "); 715 break; 716 case 16: // xzH hysterisis 717 StdFlloatValueDisplay(xzH,"X Hysteresis "); 718 break; 719 case 15: // yOffset 720 StdFlloatValueDisplay(yzOffset,"Y Offset "); 721 break; 722 case 14: // xOffset 723 StdFlloatValueDisplay(xzOffset,"X Offset "); 724 break; 725 case 13: 726 lcd.setCursor ( 0, 1 ); // line 1 727 lcd.print(iTrackMode) ; 728 lcd.print( " - ") ; 729 PrintTrackerMode(iTrackMode); 730 lcd.setCursor ( 0, 2 ); // line 2 731 lcd.print( "PB change track mode") ; 732 break; 733 case 12: 734 StdFlloatValueDisplay(dxPark,"X Park "); 735 736 break; 737 case 11: 738 StdFlloatValueDisplay(dyPark,"Y Park "); 739 740 break; 741 case 10: 742 StdFlloatValueDisplay(yzTarget,"Y "); 743 lcd.print( " WE angle") ; 744 break; 745 case 9: 746 StdFlloatValueDisplay(xzTarget,"X "); 747 lcd.print( " NS angle") ; 748 break; 749 case 8: 750 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 751 lcd.print(buff) ; 752 lcd.setCursor ( 0, 1 ); // line 2 753 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 754 lcd.print(buff) ; 755 lcd.setCursor ( 0, 2 ); // line 2 756 lcd.print( "Press PB to set time ") ; 757 if ( not digitalRead(ENCODER_PB) ) { 758 DS3231_set(t); 759 } 760 break; 761 case 1: 762 case 2: 763 case 3: 764 case 4: 765 case 5: 766 case 6: 767 case 7: 768 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 769 lcd.print(buff) ; 770 if (iMode == 7) { 771 lcd.setCursor ( 0, 1 ); 772 lcd.print( "Day - > ") ; 773 PrintDay(t.wday); 774 } 775 lcd.setCursor ( 0, 2 ); 776 lcd.print( "Hold in set") ; 777 lcd.setCursor ( 10, 3 ) ; 778 switch (iMode) { 779 case 1: 780 lcd.print( "Year") ; 781 break; 782 case 2: 783 lcd.print( "Month") ; 784 break; 785 case 3: 786 lcd.print( "Day") ; 787 break; 788 case 4: 789 lcd.print( "Hour") ; 790 break; 791 case 5: 792 lcd.print( "Min") ; 793 break; 794 case 6: 795 lcd.print( "Sec") ; 796 break; 797 case 7: 798 lcd.print( "WDay") ; 799 break; 800 } 801 // lcd.noBlink(); 802 break; 803 default: // 0 804 DS3231_get(&tc); // update the time registers 805 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 806 lcd.print(buff) ; 807 lcd.setCursor ( 0, 1 ); // line 2 has the Az and El 808 lcd.print( "Az ") ; 809 lcd.print(solar_az_deg) ; 810 lcd.setCursor ( 10, 1 ); 811 lcd.print( "El ") ; 812 lcd.print(solar_el_deg) ; 813 814 lcd.setCursor ( 0, 2 ); 815 lcd.print("D "); 816 if ( decl > 0 ) { 817 lcd.print("+"); 818 } 819 lcd.print(decl); 820 lcd.setCursor ( 10, 2 ); 821 lcd.print("H "); 822 if ( ha > 0 ) { 823 lcd.print("+"); 824 } 825 lcd.print(ha); 826 lcd.setCursor ( 0, 3 ); // third line is sunset and sunrise 827 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunrise), MinSolarTime(sunrise)); 828 lcd.print(buff) ; 829 if (iDayNight == 1) { 830 lcd.setCursor ( 8, 3 ); 831 lcd.print(" DAY ") ; 832 } else { 833 lcd.setCursor ( 7, 3 ); 834 lcd.print("NIGHT") ; 835 } 836 lcd.setCursor ( 15, 3 ); 837 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunset), MinSolarTime(sunset)); 838 lcd.print(buff) ; 839 break; 840 } 841 842 iCycle = tc.sec ; 843 DisplayMeatBall() ; 844 } 845 846 if (((tc.hour > 19 ) || ( tc.hour < 5 )) && (iTrackMode < 3)) { 847 ActivateRelays(0) ; // power down at night if in tracking mode 848 }else{ 849 ActivateRelays(1) ; 850 } 851 digitalWrite(WATCHDOG,HIGH); // nice doggy 852 853 state1 = slave1.poll( (uint16_t*)&iMode, MAX_MODBUS_DATA ); 854 855 switch (state1) { 856 case EXC_ADDR_RANGE: 857 // Serial.println("EXC_ADDR_RANGE PORT 1"); 858 break; 859 case EXC_FUNC_CODE: 860 // Serial.println("EXC_FUNC_CODE PORT 1"); 861 break; 862 case EXC_REGS_QUANT: 863 // Serial.println("EXC_REGS_QUANT PORT 1"); 864 break; 865 } 866 867} 868 869 870 871 872 873 874float DayOfYear(uint16_t iYear , uint8_t iMon , uint8_t iDay , uint8_t iHour , uint8_t iMin ) { 875 int i ; 876 float iTDay ; 877 878 iTDay = iDay - 1 ; // this is zero referenced 879 for ( i = 1 ; i < iMon ; i++ ) { 880 switch (i) { 881 case 1: 882 case 3: 883 case 5: 884 case 7: 885 case 8: 886 case 10: 887 case 12: 888 iTDay += 31 ; 889 break; 890 case 4: 891 case 6: 892 case 9: 893 case 11: 894 iTDay += 30 ; 895 break; 896 case 2 : 897 if ((iYear % 4) == 0 ) { 898 iTDay += 29 ; 899 } else { 900 iTDay += 28 ; 901 } 902 break; 903 } 904 } 905 iTDay += (( 1.0 * iHour - 12 ) / 24 ) ; 906 // iDay += 1.0 * iMin / 1440 ; 907 return (iTDay); 908} 909 910void PrintTrackerMode(int iTarget) { 911 switch (iTarget) { 912 case 1: 913 lcd.print( "EW Only") ; 914 break; 915 case 2: 916 lcd.print( "NS Only") ; 917 break; 918 case 3: 919 lcd.print( "None") ; 920 break; 921 case 4: 922 lcd.print( "Both Park") ; 923 break; 924 case -1: 925 lcd.print( "2P ") ; 926 default: 927 lcd.print( "Both Track") ; 928 break; 929 } 930} 931 932 933void PrintDay(int iTarget) { 934 switch (iTarget) { 935 case 1: 936 lcd.print( "Sun") ; 937 break; 938 case 2: 939 lcd.print( "Mon") ; 940 break; 941 case 3: 942 lcd.print( "Tue") ; 943 break; 944 case 4: 945 lcd.print( "Wed") ; 946 break; 947 case 5: 948 lcd.print( "Thr") ; 949 break; 950 case 6: 951 lcd.print( "Fri") ; 952 break; 953 case 7: 954 lcd.print( "Sat") ; 955 break; 956 } 957} 958 959int HrsSolarTime(float target) { 960 int i ; 961 i = target ; 962 return ( i / 60 ); 963} 964int MinSolarTime(float target) { 965 int i ; 966 i = target ; 967 return ( i % 60 ); 968} 969 970void counter() 971{ 972 973 if ( ( prev_millis + 100 ) < millis() ) { 974 if ( digitalRead(ENCODER_PB) ) { 975 IncInt(&iMode,digitalRead(ENCODER_PINB),1,0,24,true); 976 } else { 977 if ((iMode > 8 ) && (iMode < 23) && (iMode != 18)) { 978 iSave = 1; 979 } 980 switch (iMode) { // setting the time and date etc 981 case 1: // year 982 IncInt(&t.year ,digitalRead(ENCODER_PINB),1,2016,3116,false); 983 break; 984 case 2: // month 985 IncUint8_t(&t.mon ,digitalRead(ENCODER_PINB),1,1,12,true); 986 break; 987 case 3: // day 988 IncUint8_t(&t.mday ,digitalRead(ENCODER_PINB),1,1,31,true); 989 break; 990 case 4: // hour 991 IncUint8_t(&t.hour ,digitalRead(ENCODER_PINB),1,0,23,true); 992 break; 993 case 5: 994 IncUint8_t(&t.min ,digitalRead(ENCODER_PINB),1,0,59,true); 995 break; 996 case 6: 997 IncUint8_t(&t.sec ,digitalRead(ENCODER_PINB),1,0,59,true); 998 break; 999 case 7: 1000 IncUint8_t(&t.wday ,digitalRead(ENCODER_PINB),1,1,7,true); 1001 break; 1002 case 9: 1003 IncFloat(&xzTarget,digitalRead(ENCODER_PINB),1,0,90,false); 1004 break; 1005 case 10: 1006 IncFloat(&yzTarget,digitalRead(ENCODER_PINB),1,-80,80,false); 1007 break; 1008 case 11: 1009 IncFloat(&dyPark,digitalRead(ENCODER_PINB),1,-80,80,false); 1010 break; 1011 case 12: 1012 IncFloat(&dxPark,digitalRead(ENCODER_PINB),1,-80,80,false); 1013 break; 1014 case 13: 1015 IncInt(&iTrackMode,digitalRead(ENCODER_PINB),1,-1,4,true); 1016 break; 1017 case 14: 1018 IncFloat(&xzOffset,digitalRead(ENCODER_PINB),0.5,-20,20,false); 1019 break; 1020 case 15: 1021 IncFloat(&yzOffset,digitalRead(ENCODER_PINB),0.5,-20,20,false); 1022 break; 1023 case 16: 1024 IncFloat(&xzH,digitalRead(ENCODER_PINB),0.25,-20,20,false); 1025 break; 1026 case 17: 1027 IncFloat(&yzH,digitalRead(ENCODER_PINB),0.25,-20,20,false); 1028 break; 1029 case 18: 1030 if (( iDoSave == 0 ) && ( iSave != 0 )) { 1031 iDoSave = 2 ; 1032 } 1033 break; 1034 case 19: 1035 IncFloat(&xMinVal,digitalRead(ENCODER_PINB),1.0,-10,50,false); 1036 break; 1037 case 20: 1038 IncFloat(&xMaxVal,digitalRead(ENCODER_PINB),1.0,-10,50,false); 1039 break; 1040 case 21: 1041 IncFloat(&yMinVal,digitalRead(ENCODER_PINB),1.0,-70,50,false); 1042 break; 1043 case 22: 1044 IncFloat(&yMaxVal,digitalRead(ENCODER_PINB),1.0,-70,50,false); 1045 break; 1046 } 1047 } 1048 prev_millis = millis(); 1049 } else { 1050 if ( prev_millis > ( millis() + 1000 ) ) { 1051 prev_millis = millis(); 1052 } 1053 } 1054 1055} 1056 1057float sign(float target) { 1058 if (target > 0 ) { 1059 return (1); 1060 } else { 1061 if (target < 0 ) { 1062 return (-1); 1063 } else { 1064 return (0); 1065 } 1066 } 1067} 1068 1069void DisplayMeatBall() { 1070 int pos , led , x , y; 1071 float dx , dy ; 1072 float dxa , dya ; 1073 1074 HT.setBrightness(15); 1075 1076 dx = xzAng - xzTarget ; 1077 dy = yzAng - yzTarget ; 1078 dxa = abs(dx) ; 1079 dya = abs(dy) ; 1080 if (dxa < 6) { 1081 x = 0 ; 1082 } else { 1083 if (dxa < 12) { 1084 x = sign(dx); 1085 } else { 1086 if (dxa < 50) { 1087 x = 2 * sign(dx); 1088 } else { 1089 x = 3 * sign(dx); 1090 } 1091 } 1092 } 1093 if (dya < 6) { 1094 y = 0 ; 1095 } else { 1096 if (dya < 12) { 1097 y = sign(dy); 1098 } else { 1099 if (dya < 25) { 1100 y = 2 * sign(dy); 1101 } else { 1102 y = 3 * sign(dy); 1103 } 1104 } 1105 } 1106 pos = 27 ; // netral position 1107 pos += (y * 8) ; // add or sumtract the x in range of -3 to +3 1108 pos += (x ) ; // add or sumtract 8 * y or y in range of -3 to +3 1109 for (led = 0; led < 63; led++) { 1110 switch (led){ 1111 case 0: 1112 case 7: 1113 case 56: 1114 case 63: 1115 break; 1116 default: 1117 HT.clearLedNow(MapLedNo(led)); 1118 break; 1119 } 1120 } 1121 1122 HT.setLedNow(MapLedNo(0)); // turn on four courners 1123 HT.setLedNow(MapLedNo(7)); 1124 HT.setLedNow(MapLedNo(56)); 1125 HT.setLedNow(MapLedNo(63)); 1126 1127 1128 // HT.setLedNow(MapLedNo(tc.sec)); 1129 if ((iPWM_YZ == 0) && (iPWM_XZ == 0)) { 1130 HT.setBlinkRate(HT16K33_DSP_NOBLINK); // not attempting to move 1131 if (tc.sec % 2 == 0 ) { 1132 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1133 HT.setLedNow(MapLedNo(pos + 9)); 1134 }else{ 1135 HT.setLedNow(MapLedNo(pos + 1)); 1136 HT.setLedNow(MapLedNo(pos + 8)); 1137 } 1138 } else { 1139 HT.setBlinkRate(HT16K33_DSP_BLINK2HZ); //moving so blink meat ball 1140 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1141 HT.setLedNow(MapLedNo(pos + 1)); 1142 HT.setLedNow(MapLedNo(pos + 8)); 1143 HT.setLedNow(MapLedNo(pos + 9)); 1144 } 1145} 1146 1147 1148int MapLedNo(int target) // this compensates for the screwy setup of the matrix driver to the chip 1149{ 1150 int row ; 1151 int col ; 1152 row = target / 8 ; 1153 col = target % 8 ; 1154 if (col == 0 ) { 1155 return ((row * 16 ) + 7) ; 1156 } else { 1157 return ((row * 16 ) + col - 1) ; 1158 } 1159} 1160 1161
TRACKER_ALL_IN_ONE_BOTTOM.ino
c_cpp
This for a "Uno" Board using the described hardware There are 24 ish screens of data and diagnostics .... uses the chips internal eeprom for non-volatile storage. Good coding exercise as I has do be more efficient as the program grew...It been a long time since I have had only 32K to program in . Don't stress about the modified lib's - they are only patches to faults in the coding. If you need just ask....
1//#include <SFE_BMP180.h> 2#include <avr/wdt.h> 3#include <Wire.h> 4#include <LSM303.h> // modified ... fixed a couple of bugs 5#include <LiquidCrystal_I2C.h> 6//#include <L3G.h> 7#include "ds3231.h" 8#include "ht16k33.h" 9#include <ModbusRtu.h> // Modified ... this no longer a stock lib - Slave supports register translation/mapping 10#include <EEPROM.h> 11#include <math.h> 12 13#define ID 1 14 15#define BUFF_MAX 32 16#define PARK_EAST 1 17#define PARK_WEST 2 18#define PARK_NORTH 3 19#define PARK_SOUTH 4 20#define PARK_FLAT 5 21 22 23#define MAX_MODBUS_DATA 60 24 25#define HT16K33_DSP_NOBLINK 0 // constants for the half arsed cheapo display 26#define HT16K33_DSP_BLINK1HZ 4 27#define HT16K33_DSP_BLINK2HZ 2 28#define HT16K33_DSP_BLINK05HZ 6 29 30 31const byte ENCODER_PINA = 2; // encoder connects - interupt pin 32const byte ENCODER_PINB = 3; // non interupt pin 33const byte ENCODER_PB = 8; 34 35const byte RELAY_XZ_F = 4; // X+ Relay 1 North 36const byte RELAY_XZ_R = 5; // X- Relay 2 South 37const byte RELAY_YZ_F = 6; // Y+ Relay 3 West 38const byte RELAY_YZ_R = 7; // Y- Relay 4 East 39 40const byte UNUSED09 = 9; 41const byte UNUSED10 = 10; 42const byte UNUSED11 = 11; 43const byte WATCHDOG = 12; 44 45 46//L3G gyro; 47LSM303 compass; 48LiquidCrystal_I2C lcd(0x27); // Set the LCD I2C address I modified the lib for default wiring 49 50//SFE_BMP180 pressure; 51HT16K33 HT; 52 53Modbus slave1(ID, 1, 0); // this is slave ID and RS-232 or USB-FTDI 54 55uint8_t time[8]; 56 57char recv[BUFF_MAX]; 58unsigned int recv_size = 0; 59unsigned long prev_millis; 60uint8_t u8state; //!< machine state 61uint8_t u8query; //!< pointer to message query 62 63struct ts t; // MODBUS MAP 64struct ts tc; // 44 65int iSave = 0 ; // 43 66int iDoSave = 0 ; // 42 67float T; // 40 temperature of board 68float xzTarget ; // 38 target for angles 69float yzTarget ; // 36 70float xzH ; // 34 hyserisis zone 71float yzH ; // 32 72float xzAng; // 30 current angles 73float yzAng; // 28 74float xzOffset; // 26 offset xz 75float yzOffset; // 24 offset yz 76float dyPark; // 22 parking position 77float dxPark; // 20 78float xMinVal ; // 18 Min and Max values X - N/S 79float xMaxVal ; // 16 80float yMinVal ; // 14 Y -- E/W 81float yMaxVal ; // 12 82float latitude; // 10 83float longitude; // 8 84int timezone; // 7 85int iDayNight ; // 6 86float solar_az_deg; // 4 87float solar_el_deg; // 2 88int iTrackMode ; // 1 89int iMode ; // 0 90 91int iCycle ; 92int iPMode; 93 94float baseline; // baseline pressure 95float gT ; 96unsigned long tempus; 97int8_t state1 = 0; 98 99 100void ActivateRelays(int iAllStop) { 101 if ( iAllStop == 0 ) { 102 digitalWrite(RELAY_YZ_R, HIGH) ; 103 digitalWrite(RELAY_YZ_F, HIGH) ; 104 digitalWrite(RELAY_XZ_R, HIGH) ; 105 digitalWrite(RELAY_XZ_F, HIGH) ; 106 } else { 107 if ((tc.sec % 5) == 0 ){ 108 if (((yzAng ) < ( yzTarget - yzH )) && ( digitalRead(RELAY_YZ_F) == HIGH )) { // do Y ie E/W before N/S 109 digitalWrite(RELAY_YZ_F, LOW) ; 110 digitalWrite(RELAY_YZ_R, HIGH) ; 111 } 112 if (((yzAng ) > (yzTarget + yzH )) && ( digitalRead(RELAY_YZ_R) == HIGH )) { 113 digitalWrite(RELAY_YZ_F, HIGH) ; 114 digitalWrite(RELAY_YZ_R, LOW) ; 115 } 116 } 117 if ((yzAng > yzTarget) && ( digitalRead(RELAY_YZ_F) == LOW )) { 118 digitalWrite(RELAY_YZ_F, HIGH) ; 119 digitalWrite(RELAY_YZ_R, HIGH) ; 120 } 121 if ((yzAng < yzTarget) && ( digitalRead(RELAY_YZ_R) == LOW )) { 122 digitalWrite(RELAY_YZ_F, HIGH) ; 123 digitalWrite(RELAY_YZ_R, HIGH) ; 124 } 125 126 if (( digitalRead(RELAY_YZ_F) == HIGH ) && ( digitalRead(RELAY_YZ_R) == HIGH )) { // if finished on E/W you can do N/S 127 if ((tc.sec % 5) == 0 ){ 128 if ((xzAng < ( xzTarget - xzH )) && ( digitalRead(RELAY_XZ_F) == HIGH )) { // turn on if not in tolerance 129 digitalWrite(RELAY_XZ_F, LOW) ; 130 digitalWrite(RELAY_XZ_R, HIGH) ; 131 } 132 if ((xzAng > ( xzTarget + xzH ))&& ( digitalRead(RELAY_XZ_R) == HIGH )) { // turn on if not in tolerance 133 digitalWrite(RELAY_XZ_F, HIGH) ; 134 digitalWrite(RELAY_XZ_R, LOW) ; 135 } 136 } 137 }else{ 138 if ((digitalRead(RELAY_XZ_F) == LOW ) || (digitalRead(RELAY_XZ_R) == LOW )){ 139 digitalWrite(RELAY_XZ_F, HIGH) ; // switch off if on 140 digitalWrite(RELAY_XZ_R, HIGH) ; 141 } 142 } 143 if ((xzAng > xzTarget ) && ( digitalRead(RELAY_XZ_F) == LOW )) { // if on turn off 144 digitalWrite(RELAY_XZ_F, HIGH) ; 145 digitalWrite(RELAY_XZ_R, HIGH) ; 146 } 147 if ((xzAng < xzTarget ) && ( digitalRead(RELAY_XZ_R) == LOW )) { // if on turn off 148 digitalWrite(RELAY_XZ_F, HIGH) ; 149 digitalWrite(RELAY_XZ_R, HIGH) ; 150 } 151 } 152} 153 154void FloatToModbusWords(float src_value , uint16_t * dest_lo , uint16_t * dest_hi ) { 155 uint16_t tempdata[2] ; 156 float *tf ; 157 tf = (float * )&tempdata[0] ; 158 *tf = src_value ; 159 *dest_lo = tempdata[1] ; 160 *dest_hi = tempdata[0] ; 161} 162float FloatFromModbusWords( uint16_t dest_lo , uint16_t dest_hi ) { 163 uint16_t tempdata[2] ; 164 float *tf ; 165 tf = (float * )&tempdata[0] ; 166 tempdata[1] = dest_lo ; 167 tempdata[0] = dest_hi ; 168 return (*tf) ; 169} 170 171// Arduino doesnt have these to we define from a sandard libruary 172float arcsin(float x) { 173 return (atan(x / sqrt(-x * x + 1))); 174} 175float arccos(float x) { 176 return (atan(x / sqrt(-x * x + 1)) + (2 * atan(1))); 177} 178// fractional orbital rotation in radians 179float gama(struct ts *tm) { 180 return ((2 * PI / 365 ) * DayOfYear(tm->year , tm->mon , tm->mday , tm->hour , tm->min )); 181} 182// equation of rime 183float eqTime(float g) { 184 return (229.18 * ( 0.000075 + ( 0.001868 * cos(g)) - (0.032077 * sin(g)) - (0.014615 * cos (2 * g)) - (0.040849 * sin(2 * g)))); 185} 186// declination of sun in radians 187float Decl(float g) { 188 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)) ); 189} 190float TimeOffset(float longitude , struct ts *tm , int timezone ) { 191 float dTmp ; 192 dTmp = (-4.0 * longitude ) + (60 * timezone) - eqTime(gama(tm)) ; 193 return (dTmp); 194} 195 196float TrueSolarTime(float longitude , struct ts *tm , int timezone ) { 197 float dTmp ; 198 dTmp = ( 60.0 * tm->hour ) + (1.0 * tm->min) + (1.0 * tm->sec / 60) - TimeOffset(longitude, tm, timezone) ; 199 return (dTmp); 200} 201float HourAngle(float longitude , struct ts *tm , int timezone) { 202 float dTmp; 203 dTmp = (TrueSolarTime(longitude, tm, timezone) / 4 ) - 180 ; // 720 minutes is solar noon -- div 4 is 180 204 return (dTmp); 205} 206// Hour angle for sunrise and sunset only 207float HA (float lat , struct ts *tm ) { 208 float latRad ; 209 latRad = lat * 2 * PI / 360 ; 210 return ( acos((cos(90.833 * PI / 180 ) / ( cos(latRad) * cos(Decl(gama(tm)))) - (tan(latRad) * tan(Decl(gama(tm)))))) / PI * 180 ); 211} 212 213float Sunrise(float longitude , float lat , struct ts *tm , int timezone) { 214 return (720 - ( 4.0 * (longitude + HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 215} 216float Sunset(float longitude , float lat , struct ts *tm , int timezone) { 217 return (720 - ( 4.0 * (longitude - HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 218} 219float SNoon(float longitude , float lat , struct ts *tm , int timezone) { 220 return (720 - ( 4.0 * (longitude + (60 * timezone) - eqTime(gama(tm)))) ) ; 221} 222 223float SolarZenithRad(float longitude , float lat , struct ts *tm , int timezone) { 224 float latRad ; 225 float decRad ; 226 float HourAngleRad ; 227 float dTmp ; 228 229 latRad = lat * 2 * PI / 360 ; 230 decRad = Decl(gama(tm)); 231 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 232 dTmp = acos((sin(latRad) * sin(decRad)) + (cos(latRad) * cos(decRad) * cos(HourAngleRad))); 233 return (dTmp) ; 234 235} 236float SolarElevationRad(float longitude , float lat , struct ts *tm , int timezone ) { 237 return ((PI / 2) - SolarZenithRad(longitude , lat , tm , timezone )) ; 238} 239 240float SolarAzimouthRad(float longitude , float lat , struct ts *tm , int timezone) { 241 float latRad ; 242 float decRad ; 243 float solarzenRad ; 244 float HourAngleRad ; 245 float dTmp ; 246 latRad = lat * 2 * PI / 360 ; 247 decRad = Decl(gama(tm)); 248 solarzenRad = SolarZenithRad ( longitude , lat , tm , timezone ) ; 249 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 250 dTmp = acos(((sin(decRad) * cos(latRad)) - (cos(HourAngleRad) * cos(decRad) * sin(latRad))) / sin(solarzenRad)) ; 251 if ( HourAngleRad < 0 ) { 252 return (dTmp) ; 253 } else { 254 return ((2 * PI) - dTmp) ; 255 } 256} 257 258float LoadFloatFromEEPROM(int address,float minval,float maxval, float defaultval){ 259float tmp ; 260 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 261 if (( tmp < minval ) || ( tmp > maxval )|| (NumberOK(tmp) == 1)) { 262 tmp = defaultval ; 263 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 264 } 265 return(tmp); 266} 267int LoadIntFromEEPROM(int address,int minval,int maxval, int defaultval){ 268int tmp ; 269 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 270 if (( tmp < minval ) || ( tmp > maxval )) { 271 tmp = defaultval ; 272 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 273 } 274 return(tmp); 275} 276 277int NumberOK (float target) { 278 int tmp = 0 ; 279 tmp = isnan(target); 280 if ( tmp != 1 ) { 281 tmp = isinf(target); 282 } 283 return (tmp); 284} 285 286void IncFloat(float * target, boolean bInc, float increment, float minval, float maxval , boolean bWrap){ 287 if( bInc ){ 288 *target += increment ; 289 }else{ 290 *target -= increment ; 291 } 292 if ( *target > maxval ){ 293 if ( bWrap ){ 294 *target = minval ; 295 }else{ 296 *target = maxval ; 297 } 298 } 299 if ( *target < minval ){ 300 if ( bWrap ){ 301 *target = maxval ; 302 }else{ 303 *target = minval ; 304 } 305 } 306} 307void IncInt(int * target, boolean bInc, int increment, int minval, int maxval , boolean bWrap){ 308 if( bInc ){ 309 *target += increment ; 310 }else{ 311 *target -= increment ; 312 } 313 if ( *target > maxval ){ 314 if ( bWrap ){ 315 *target = minval ; 316 }else{ 317 *target = maxval ; 318 } 319 } 320 if ( *target < minval ){ 321 if ( bWrap ){ 322 *target = maxval ; 323 }else{ 324 *target = minval ; 325 } 326 } 327} 328void IncUint8_t (uint8_t * target , boolean bInc, int increment, int minval, int maxval , boolean bWrap){ 329 if( bInc ){ 330 *target += increment ; 331 }else{ 332 *target -= increment ; 333 } 334 if ( *target > maxval ){ 335 if ( bWrap ){ 336 *target = minval ; 337 }else{ 338 *target = maxval ; 339 } 340 } 341 if ( *target < minval ){ 342 if ( bWrap ){ 343 *target = maxval ; 344 }else{ 345 *target = minval ; 346 } 347 } 348} 349void StdFlloatValueDisplay(float target, char message[]){ 350 lcd.setCursor ( 0, 1 ); // line 1 351 lcd.print(message) ; 352 lcd.print(target) ; 353 lcd.setCursor ( 0, 2 ); // line 2 354 lcd.print( "PB to set ") ; 355} 356 357 358void setup() { 359 int led ; 360 361 Wire.begin(); 362 lcd.begin(20, 4); 363 lcd.home(); 364 lcd.setBacklightPin(3, NEGATIVE); 365 lcd.noCursor(); 366 367 MCUSR &= ~_BV(WDRF); 368 wdt_disable(); 369 370 compass.init(); 371 compass.enableDefault(); 372 compass.setTimeout(1000); 373 374 pinMode(RELAY_XZ_F, OUTPUT); // relay 375 pinMode(RELAY_XZ_R, OUTPUT); // relay 376 pinMode(RELAY_YZ_F, OUTPUT); // relay 377 pinMode(RELAY_YZ_R, OUTPUT); // relay 378 pinMode(13, OUTPUT); // 379 digitalWrite(13, HIGH ); 380 ActivateRelays(0); // call an all stop first 381 382 Serial.begin(9600) ; // use as a diagnostic port 383 slave1.begin( 9600 ); // RS-232 to base of tower 384 tempus = millis() + 100; 385 386 pinMode(ENCODER_PINA, INPUT_PULLUP); 387 pinMode(ENCODER_PINB, INPUT_PULLUP); 388 pinMode(ENCODER_PB, INPUT_PULLUP); 389 attachInterrupt(0, counter, FALLING); 390 391 pinMode(UNUSED09, OUTPUT); // unused so dont leave floating set as output 392 pinMode(UNUSED10, OUTPUT); // 393 pinMode(UNUSED11, OUTPUT); // 394 pinMode(WATCHDOG, OUTPUT); // 395 digitalWrite(WATCHDOG,HIGH); 396 397 compass.m_min = (LSM303::vector<int16_t>) {-3848, -1822, -1551 }; // calibration figures are empirical 398 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300}; 399 400 xzH = LoadFloatFromEEPROM(0,0.1,20.0,4.0); // hysterisis NS 401 yzH = LoadFloatFromEEPROM(1,0.1,20.0,4.0); // "" EW 402 403 dyPark = LoadFloatFromEEPROM(2,-70.0,50.0,0); 404 dxPark = LoadFloatFromEEPROM(3,-5.0,50.0,0.0); 405 406 xzOffset = LoadFloatFromEEPROM(4,-90.0,90.0,0); // NS 407 yzOffset = LoadFloatFromEEPROM(5,-90.0,90.0,0); // EW 408 409 xzTarget = LoadFloatFromEEPROM(6,-90.0,90.0,0); // NS 410 yzTarget = LoadFloatFromEEPROM(7,-90.0,90.0,0); // EW 411 412 xMinVal = LoadFloatFromEEPROM(8,-10.0,60.0,0.0); // NS 413 xMaxVal = LoadFloatFromEEPROM(9,-10.0,60.0,45); 414 415 yMinVal = LoadFloatFromEEPROM(10,-70.0,50.0,-65); // EW 416 yMaxVal = LoadFloatFromEEPROM(11,-70.0,50.0,45); 417 418 iMode = 0 ; 419 iTrackMode = LoadIntFromEEPROM(12,-1,4,0); 420 421// latitude = -34.051219 ; 422// longitude = 142.013618 ; 423// timezone = 10 ; 424 425 latitude = LoadFloatFromEEPROM(13,-90.0,90.0,-34.051219); 426 longitude = LoadFloatFromEEPROM(14,-180.0,180.0,142.013618); 427 timezone = LoadIntFromEEPROM(15,0,23,10); 428 429 DS3231_init(DS3231_INTCN); 430 DS3231_get(&tc); 431 DS3231_get(&t); 432 lcd.clear(); 433 HT.begin(0x00); 434 for (led = 0; led < 127; led++) { 435 HT.clearLedNow(led); 436 } 437 wdt_enable(WDTO_4S); 438} 439 440void loop() { 441 float P; 442 float sunInc; 443 float sunAng; 444 float xzRatio; 445 float yzRatio; 446 char buff[BUFF_MAX]; 447 float decl ; 448 float eqtime ; 449 float dTmp ; 450 float ha ; 451 float heading ; 452 float sunrise ; 453 float sunset ; 454 float tst ; 455 float sunX ; 456 457 compass.read(); // this reads all 6 channels 458// compass.readAcc(); 459// compass.readMag(); 460 digitalWrite(UNUSED09,!digitalRead(UNUSED09)); // toggle this output so I can measure the cycle time with a scope 461 462 T = DS3231_get_treg(); 463 heading = compass.heading((LSM303::vector<int>) { 1, 0, 0 }); 464 465 if (( compass.a.z != 0) && (!compass.timeoutOccurred() )) { 466 xzRatio = (float)compass.a.y / abs((float)compass.a.z) ; // yep if your sharp you can see I swapped axis orientation late in the programming 467 yzRatio = (float)compass.a.x / abs((float)compass.a.z) ; 468 xzAng = ((float)atan(xzRatio) / PI * -180 ) + xzOffset ; // good old offsets or fudge factors 469 yzAng = ((float)atan(yzRatio) / PI * 180 ) + yzOffset ; 470 }else{ // try restarting the compass/accelerometer modual - cos he gone walkabout... 471 compass.init(); 472 compass.enableDefault(); 473 compass.setTimeout(1000); // BTW I fixed up the int / long issue in the time out function in the LM303 lib I was using 474 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 !!) 475 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300 }; 476 } 477 478 DS3231_get(&tc); 479 480 solar_az_deg = SolarAzimouthRad(longitude, latitude, &tc, timezone) * 180 / PI ; 481 solar_el_deg = SolarElevationRad(longitude, latitude, &tc, timezone) * 180 / PI ; 482 483 decl = Decl(gama(&tc)) * 180 / PI ; 484 ha = HourAngle (longitude , &tc , timezone ) ; 485 sunrise = Sunrise(longitude, latitude, &tc, timezone) ; 486 sunset = Sunset(longitude, latitude, &tc, timezone); 487 tst = TrueSolarTime(longitude, &tc, timezone); 488 sunX = abs(latitude) + decl ; 489 if (solar_el_deg >= 0 ){ // day 490 iDayNight = 1 ; 491 }else{ // night 492 iDayNight = 0 ; 493 } 494 495 switch (iTrackMode) { 496 case 4: // both axis to park 497 yzTarget = dyPark ; // night park position E/W 498 xzTarget = dxPark ; // night park position N/S 499 break ; 500 case 3: // both axis off no tracking 501 break ; 502 case 2: // xz tracking NS 503 if ( iDayNight == 1 ) { 504 xzTarget = sunX ; // need to map the coordinate system correctly 505 } else { 506 xzTarget = dxPark ; // night park position 507 } 508 break; 509 case 1: // yz tracking EW 510 if (iDayNight == 1) { 511 yzTarget = ha ; 512 } else { 513 yzTarget = dyPark ; // night park position 514 } 515 break; 516 case -1: // set target to tracking and park both at nigh 517 if (iDayNight == 1) { 518 yzTarget = ha ; 519 xzTarget = sunX ; // need to map the coordinate system correctly 520 } else { 521 yzTarget = dyPark ; // night park position E/W 522 xzTarget = dxPark ; // night park position N/S 523 } 524 break; 525 default: // set target to tracking 526 if (iDayNight == 1) { 527 yzTarget = ha ; 528 xzTarget = sunX ; // need to map the coordinate system correctly 529 } else { 530 yzTarget = dyPark ; // night park position (dont park the other - leave till morning) 531 } 532 break; 533 } 534 xzTarget = constrain(xzTarget,xMinVal,xMaxVal); // constain function... very cool - dont leave home without it ! 535 yzTarget = constrain(yzTarget,yMinVal,yMaxVal); 536 537 if ( iPMode != iMode ) { // clear screen after first change of mode 538 lcd.clear(); 539 if ((( iPMode < 1 ) || ( iPMode > 8 )) && ( iMode > 0 ) && (iMode < 9 )) { 540 DS3231_get(&t); // when going into time edit mode update the time - then leave it allone to be worked on by the editor 541 } 542 iPMode = iMode ; 543 } 544 if ( iCycle != tc.sec ) { //only update once a second 545 digitalWrite(WATCHDOG,LOW); // external watch dog pin 546 wdt_reset(); // reset internal watchdog - good puppy 547 // lcd.clear(); 548 if ( iMode > 0 ){ 549 lcd.setCursor ( 0, 3 ); // line 3 550 lcd.print( "Mode ") ; 551 lcd.print(iMode) ; 552 lcd.print( " " ) ; 553 switch (tc.sec % 2 ){ 554 case 1: 555 lcd.print( "|" ) ; 556 break; 557 default: 558 lcd.print( "-" ) ; 559 break; 560 } 561 } 562 lcd.setCursor ( 0, 0 ); // line 0 563 switch (iMode) { 564 case 22: 565 StdFlloatValueDisplay(yMaxVal,"Y Max "); 566 break; 567 case 21: 568 StdFlloatValueDisplay(yMinVal,"Y Min "); 569 break; 570 case 20: 571 StdFlloatValueDisplay(xMaxVal,"X Max "); 572 break; 573 case 19: 574 StdFlloatValueDisplay(xMinVal,"X Min "); 575 break; 576 case 23: 577 lcd.setCursor ( 0, 0 ); 578 lcd.print("Lat "); 579 lcd.print(latitude,6); 580 lcd.setCursor ( 0, 1 ); 581 lcd.print("Long "); 582 lcd.print(longitude,6); 583 lcd.setCursor ( 0, 2 ); 584 lcd.print("Time Zone "); 585 lcd.print(timezone); 586 break; 587 case 24: 588 // lcd.clear(); 589 lcd.setCursor ( 0, 0 ); 590 lcd.print("X/Z "); 591 if ( xzAng > 0 ) { 592 lcd.print("+"); 593 } 594 lcd.print(xzAng); 595 lcd.setCursor ( 10, 0 ); 596 lcd.print("Y/Z "); 597 if ( yzAng > 0 ) { 598 lcd.print("+"); 599 } 600 lcd.print(yzAng); 601 lcd.setCursor ( 0, 1 ); 602 lcd.print("TX "); 603 if (( xzTarget) > 0 ) { 604 lcd.print("+"); 605 } 606 lcd.print(( xzTarget)); 607 lcd.setCursor ( 10, 1 ); 608 lcd.print("TY "); 609 if (( yzTarget) > 0 ) { 610 lcd.print("+"); 611 } 612 lcd.print(( yzTarget)); 613 lcd.setCursor ( 0, 2 ); 614 lcd.print("DX "); 615 dTmp = ( xzAng - xzTarget) ; 616 if (dTmp > 0 ) { 617 lcd.print("+"); 618 } 619 lcd.print(dTmp); 620 lcd.setCursor ( 10, 2 ); 621 lcd.print("DY "); 622 dTmp = ( yzAng - yzTarget) ; 623 if (dTmp > 0 ) { 624 lcd.print("+"); 625 } 626 lcd.print(dTmp); 627 628 lcd.setCursor ( 10, 3 ); // line 2 629 lcd.print( "T ") ; 630 lcd.print(T) ; 631 632 lcd.setCursor ( 18, 3 ); // line 2 633 if (( digitalRead(RELAY_YZ_F) == LOW )) { 634 lcd.print( "W") ; 635 }else{ 636 if (( digitalRead(RELAY_YZ_R) == LOW )) { 637 lcd.print( "E") ; 638 }else{ 639 lcd.print( " ") ; 640 } 641 } 642 lcd.setCursor ( 19, 3 ); // line 2 643 if (( digitalRead(RELAY_XZ_F) == LOW )) { 644 lcd.print( "N") ; 645 }else{ 646 if (( digitalRead(RELAY_XZ_R) == LOW )) { 647 lcd.print( "S") ; 648 }else{ 649 lcd.print( " ") ; 650 } 651 } 652 653 break; 654 case 18: // SAVE ALL 655 lcd.setCursor ( 0, 1 ); // line 1 656 if ( iSave != 0 ) { 657 lcd.print( "SAVE REQUIRED") ; 658 } else { 659 lcd.print( "### SAVED ###") ; 660 } 661 if ( iDoSave == 2 ) { 662 EEPROM.put( 0 , xzH ); 663 EEPROM.put(0 + (1 * sizeof(float)) , yzH ); 664 EEPROM.put(0 + (2 * sizeof(float)) , dyPark ); 665 EEPROM.put(0 + (3 * sizeof(float)) , dxPark ); 666 EEPROM.put(0 + (4 * sizeof(float)) , xzOffset ); 667 EEPROM.put(0 + (5 * sizeof(float)) , yzOffset ); 668 EEPROM.put(0 + (6 * sizeof(float)) , xzTarget ); 669 EEPROM.put(0 + (7 * sizeof(float)) , yzTarget ); 670 EEPROM.put(0 + (8 * sizeof(float)) , xMinVal ); 671 EEPROM.put(0 + (9 * sizeof(float)) , xMaxVal ); 672 EEPROM.put(0 + (10 * sizeof(float)) , yMinVal ); 673 EEPROM.put(0 + (11 * sizeof(float)) , yMaxVal ); 674 EEPROM.put(0 + (12 * sizeof(float)) , iTrackMode ); 675 EEPROM.put(0 + (13 * sizeof(float)) , latitude ); 676 EEPROM.put(0 + (14 * sizeof(float)) , longitude ); 677 EEPROM.put(0 + (15 * sizeof(float)) , timezone ); 678 iDoSave = 0 ; 679 iSave = 0 ; 680 } 681 lcd.setCursor ( 0, 2 ); // line 2 682 lcd.print( "PB to Save ALL") ; 683 break ; 684 case 17: // yzH hysterisis 685 StdFlloatValueDisplay(yzH,"Y Hysteresis "); 686 break; 687 case 16: // xzH hysterisis 688 StdFlloatValueDisplay(xzH,"X Hysteresis "); 689 break; 690 case 15: // yOffset 691 StdFlloatValueDisplay(yzOffset,"Y Offset "); 692 break; 693 case 14: // xOffset 694 StdFlloatValueDisplay(xzOffset,"X Offset "); 695 break; 696 case 13: 697 lcd.setCursor ( 0, 1 ); // line 1 698 lcd.print(iTrackMode) ; 699 lcd.print( " - ") ; 700 PrintTrackerMode(iTrackMode); 701 lcd.setCursor ( 0, 2 ); // line 2 702 lcd.print( "PB change track mode") ; 703 break; 704 case 12: 705 StdFlloatValueDisplay(dxPark,"X Park "); 706 707 break; 708 case 11: 709 StdFlloatValueDisplay(dyPark,"Y Park "); 710 711 break; 712 case 10: 713 StdFlloatValueDisplay(yzTarget,"Y "); 714 lcd.print( " WE angle") ; 715 break; 716 case 9: 717 StdFlloatValueDisplay(xzTarget,"X "); 718 lcd.print( " NS angle") ; 719 break; 720 case 8: 721 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 722 lcd.print(buff) ; 723 lcd.setCursor ( 0, 1 ); // line 2 724 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 725 lcd.print(buff) ; 726 lcd.setCursor ( 0, 2 ); // line 2 727 lcd.print( "Press PB to set time ") ; 728 if ( not digitalRead(ENCODER_PB) ) { 729 DS3231_set(t); 730 } 731 break; 732 case 1: 733 case 2: 734 case 3: 735 case 4: 736 case 5: 737 case 6: 738 case 7: 739 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 740 lcd.print(buff) ; 741 if (iMode == 7) { 742 lcd.setCursor ( 0, 1 ); 743 lcd.print( "Day - > ") ; 744 PrintDay(t.wday); 745 } 746 lcd.setCursor ( 0, 2 ); 747 lcd.print( "Hold in set") ; 748 lcd.setCursor ( 10, 3 ) ; 749 switch (iMode) { 750 case 1: 751 lcd.print( "Year") ; 752 break; 753 case 2: 754 lcd.print( "Month") ; 755 break; 756 case 3: 757 lcd.print( "Day") ; 758 break; 759 case 4: 760 lcd.print( "Hour") ; 761 break; 762 case 5: 763 lcd.print( "Min") ; 764 break; 765 case 6: 766 lcd.print( "Sec") ; 767 break; 768 case 7: 769 lcd.print( "WDay") ; 770 break; 771 } 772 // lcd.noBlink(); 773 break; 774 default: // 0 775 DS3231_get(&tc); // update the time registers 776 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 777 lcd.print(buff) ; 778 lcd.setCursor ( 0, 1 ); // line 2 has the Az and El 779 lcd.print( "Az ") ; 780 lcd.print(solar_az_deg) ; 781 lcd.setCursor ( 10, 1 ); 782 lcd.print( "El ") ; 783 lcd.print(solar_el_deg) ; 784 785 lcd.setCursor ( 0, 2 ); 786 lcd.print("D "); 787 if ( decl > 0 ) { 788 lcd.print("+"); 789 } 790 lcd.print(decl); 791 lcd.setCursor ( 10, 2 ); 792 lcd.print("H "); 793 if ( ha > 0 ) { 794 lcd.print("+"); 795 } 796 lcd.print(ha); 797 lcd.setCursor ( 0, 3 ); // third line is sunset and sunrise 798 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunrise), MinSolarTime(sunrise)); 799 lcd.print(buff) ; 800 if (iDayNight == 1) { 801 lcd.setCursor ( 8, 3 ); 802 lcd.print(" DAY ") ; 803 } else { 804 lcd.setCursor ( 7, 3 ); 805 lcd.print("NIGHT") ; 806 } 807 lcd.setCursor ( 15, 3 ); 808 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunset), MinSolarTime(sunset)); 809 lcd.print(buff) ; 810 break; 811 } 812 813 iCycle = tc.sec ; 814 DisplayMeatBall() ; 815 if (tc.sec % 2 == 0 ) { 816 digitalWrite(13, HIGH); 817 } else { 818 digitalWrite(13, LOW); 819 } 820 } 821 822 if (((tc.hour > 19 ) || ( tc.hour < 5 )) && (iTrackMode < 3)) { 823 ActivateRelays(0) ; // power down at night if in tracking mode 824 }else{ 825 ActivateRelays(1) ; 826 } 827 digitalWrite(WATCHDOG,HIGH); // nice doggy 828 829 state1 = slave1.poll( (uint16_t*)&iMode, MAX_MODBUS_DATA ); 830 831 switch (state1) { 832 case EXC_ADDR_RANGE: 833 // Serial.println("EXC_ADDR_RANGE PORT 1"); 834 break; 835 case EXC_FUNC_CODE: 836 // Serial.println("EXC_FUNC_CODE PORT 1"); 837 break; 838 case EXC_REGS_QUANT: 839 // Serial.println("EXC_REGS_QUANT PORT 1"); 840 break; 841 } 842 843} 844 845 846 847 848 849 850float DayOfYear(uint16_t iYear , uint8_t iMon , uint8_t iDay , uint8_t iHour , uint8_t iMin ) { 851 int i ; 852 float iTDay ; 853 854 iTDay = iDay - 1 ; // this is zero referenced 855 for ( i = 1 ; i < iMon ; i++ ) { 856 switch (i) { 857 case 1: 858 case 3: 859 case 5: 860 case 7: 861 case 8: 862 case 10: 863 case 12: 864 iTDay += 31 ; 865 break; 866 case 4: 867 case 6: 868 case 9: 869 case 11: 870 iTDay += 30 ; 871 break; 872 case 2 : 873 if ((iYear % 4) == 0 ) { 874 iTDay += 29 ; 875 } else { 876 iTDay += 28 ; 877 } 878 break; 879 } 880 } 881 iTDay += (( 1.0 * iHour - 12 ) / 24 ) ; 882 // iDay += 1.0 * iMin / 1440 ; 883 return (iTDay); 884} 885 886void PrintTrackerMode(int iTarget) { 887 switch (iTarget) { 888 case 1: 889 lcd.print( "EW Only") ; 890 break; 891 case 2: 892 lcd.print( "NS Only") ; 893 break; 894 case 3: 895 lcd.print( "None") ; 896 break; 897 case 4: 898 lcd.print( "Both Park") ; 899 break; 900 case -1: 901 lcd.print( "2P ") ; 902 default: 903 lcd.print( "Both Track") ; 904 break; 905 } 906} 907 908 909void PrintDay(int iTarget) { 910 switch (iTarget) { 911 case 1: 912 lcd.print( "Sun") ; 913 break; 914 case 2: 915 lcd.print( "Mon") ; 916 break; 917 case 3: 918 lcd.print( "Tue") ; 919 break; 920 case 4: 921 lcd.print( "Wed") ; 922 break; 923 case 5: 924 lcd.print( "Thr") ; 925 break; 926 case 6: 927 lcd.print( "Fri") ; 928 break; 929 case 7: 930 lcd.print( "Sat") ; 931 break; 932 } 933} 934 935int HrsSolarTime(float target) { 936 int i ; 937 i = target ; 938 return ( i / 60 ); 939} 940int MinSolarTime(float target) { 941 int i ; 942 i = target ; 943 return ( i % 60 ); 944} 945 946void counter() 947{ 948 949 if ( ( prev_millis + 100 ) < millis() ) { 950 if ( digitalRead(ENCODER_PB) ) { 951 IncInt(&iMode,digitalRead(ENCODER_PINB),1,0,24,true); 952 } else { 953 if ((iMode > 8 ) && (iMode < 23) && (iMode != 18)) { 954 iSave = 1; 955 } 956 switch (iMode) { // setting the time and date etc 957 case 1: // year 958 IncInt(&t.year ,digitalRead(ENCODER_PINB),1,2016,3116,false); 959 break; 960 case 2: // month 961 IncUint8_t(&t.mon ,digitalRead(ENCODER_PINB),1,1,12,true); 962 break; 963 case 3: // day 964 IncUint8_t(&t.mday ,digitalRead(ENCODER_PINB),1,1,31,true); 965 break; 966 case 4: // hour 967 IncUint8_t(&t.hour ,digitalRead(ENCODER_PINB),1,0,23,true); 968 break; 969 case 5: 970 IncUint8_t(&t.min ,digitalRead(ENCODER_PINB),1,0,59,true); 971 break; 972 case 6: 973 IncUint8_t(&t.sec ,digitalRead(ENCODER_PINB),1,0,59,true); 974 break; 975 case 7: 976 IncUint8_t(&t.wday ,digitalRead(ENCODER_PINB),1,1,7,true); 977 break; 978 case 9: 979 IncFloat(&xzTarget,digitalRead(ENCODER_PINB),1,0,90,false); 980 break; 981 case 10: 982 IncFloat(&yzTarget,digitalRead(ENCODER_PINB),1,-80,80,false); 983 break; 984 case 11: 985 IncFloat(&dyPark,digitalRead(ENCODER_PINB),1,-80,80,false); 986 break; 987 case 12: 988 IncFloat(&dxPark,digitalRead(ENCODER_PINB),1,-80,80,false); 989 break; 990 case 13: 991 IncInt(&iTrackMode,digitalRead(ENCODER_PINB),1,-1,4,true); 992 break; 993 case 14: 994 IncFloat(&xzOffset,digitalRead(ENCODER_PINB),0.5,-20,20,false); 995 break; 996 case 15: 997 IncFloat(&yzOffset,digitalRead(ENCODER_PINB),0.5,-20,20,false); 998 break; 999 case 16: 1000 IncFloat(&xzH,digitalRead(ENCODER_PINB),0.25,-20,20,false); 1001 break; 1002 case 17: 1003 IncFloat(&yzH,digitalRead(ENCODER_PINB),0.25,-20,20,false); 1004 break; 1005 case 18: 1006 if (( iDoSave == 0 ) && ( iSave != 0 )) { 1007 iDoSave = 2 ; 1008 } 1009 break; 1010 case 19: 1011 IncFloat(&xMinVal,digitalRead(ENCODER_PINB),1.0,-10,50,false); 1012 break; 1013 case 20: 1014 IncFloat(&xMaxVal,digitalRead(ENCODER_PINB),1.0,-10,50,false); 1015 break; 1016 case 21: 1017 IncFloat(&yMinVal,digitalRead(ENCODER_PINB),1.0,-70,50,false); 1018 break; 1019 case 22: 1020 IncFloat(&yMaxVal,digitalRead(ENCODER_PINB),1.0,-70,50,false); 1021 break; 1022 } 1023 } 1024 prev_millis = millis(); 1025 } else { 1026 if ( prev_millis > ( millis() + 1000 ) ) { 1027 prev_millis = millis(); 1028 } 1029 } 1030 1031} 1032 1033float sign(float target) { 1034 if (target > 0 ) { 1035 return (1); 1036 } else { 1037 if (target < 0 ) { 1038 return (-1); 1039 } else { 1040 return (0); 1041 } 1042 } 1043} 1044 1045void DisplayMeatBall() { 1046 int pos , led , x , y; 1047 float dx , dy ; 1048 float dxa , dya ; 1049 1050 if (iDayNight == 1) { 1051 HT.setBrightness(15); 1052 } else { 1053 HT.setBrightness(0); 1054 } 1055 1056 dx = xzAng - xzTarget ; 1057 dy = yzAng - yzTarget ; 1058 dxa = abs(dx) ; 1059 dya = abs(dy) ; 1060 if (dxa < 6) { 1061 x = 0 ; 1062 } else { 1063 if (dxa < 12) { 1064 x = sign(dx); 1065 } else { 1066 if (dxa < 50) { 1067 x = 2 * sign(dx); 1068 } else { 1069 x = 3 * sign(dx); 1070 } 1071 } 1072 } 1073 if (dya < 6) { 1074 y = 0 ; 1075 } else { 1076 if (dya < 12) { 1077 y = sign(dy); 1078 } else { 1079 if (dya < 25) { 1080 y = 2 * sign(dy); 1081 } else { 1082 y = 3 * sign(dy); 1083 } 1084 } 1085 } 1086 pos = 27 ; // netral position 1087 pos += (y * 8) ; // add or sumtract the x in range of -3 to +3 1088 pos += (x ) ; // add or sumtract 8 * y or y in range of -3 to +3 1089 for (led = 0; led < 63; led++) { 1090 switch (led){ 1091 case 0: 1092 case 7: 1093 case 56: 1094 case 63: 1095 break; 1096 default: 1097 HT.clearLedNow(MapLedNo(led)); 1098 break; 1099 } 1100 } 1101 1102 HT.setLedNow(MapLedNo(0)); // turn on four courners 1103 HT.setLedNow(MapLedNo(7)); 1104 HT.setLedNow(MapLedNo(56)); 1105 HT.setLedNow(MapLedNo(63)); 1106 1107 1108 // HT.setLedNow(MapLedNo(tc.sec)); 1109 if ((digitalRead(4) == HIGH) && (digitalRead(5) == HIGH) && (digitalRead(6) == HIGH) && (digitalRead(7) == HIGH)) { 1110 HT.setBlinkRate(HT16K33_DSP_NOBLINK); // not attempting to move 1111 if (tc.sec % 2 == 0 ) { 1112 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1113 HT.setLedNow(MapLedNo(pos + 9)); 1114 }else{ 1115 HT.setLedNow(MapLedNo(pos + 1)); 1116 HT.setLedNow(MapLedNo(pos + 8)); 1117 } 1118 } else { 1119 HT.setBlinkRate(HT16K33_DSP_BLINK2HZ); //moving so blink meat ball 1120 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1121 HT.setLedNow(MapLedNo(pos + 1)); 1122 HT.setLedNow(MapLedNo(pos + 8)); 1123 HT.setLedNow(MapLedNo(pos + 9)); 1124 } 1125} 1126 1127 1128int MapLedNo(int target) // this compensates for the screwy setup of the matrix driver to the chip 1129{ 1130 int row ; 1131 int col ; 1132 row = target / 8 ; 1133 col = target % 8 ; 1134 if (col == 0 ) { 1135 return ((row * 16 ) + 7) ; 1136 } else { 1137 return ((row * 16 ) + col - 1) ; 1138 } 1139} 1140 1141
Modified Modbus Library
c_cpp
Been asked a few times for my tweaked library files
1inary file (no preview
modified LM303 lib
c_cpp
very small changes aimed at roll over detection of the millis function which is used to detect time outs
1inary file (no preview
DS3231 lib
c_cpp
This is an older copy of this lib that works with this project
1inary file (no preview
Modified Modbus Library
c_cpp
Been asked a few times for my tweaked library files
1inary file (no preview
TRACKER_ALL_IN_ONE_BOTTOM_PWM_SOFT.ino
c_cpp
PWM soft starter variation on the original design ... wiring changes described in top of the file
1//#include <SFE_BMP180.h> 2#include <avr/wdt.h> 3#include <Wire.h> 4#include <LSM303.h> // modified ... fixed a couple of bugs 5#include <LiquidCrystal_I2C.h> 6//#include <L3G.h> 7#include "ds3231.h" 8#include "ht16k33.h" 9#include <ModbusRtu.h> // Modified ... this no longer a stock lib - Slave supports register translation/mapping 10#include <EEPROM.h> 11#include <math.h> 12 13#define ID 1 14 15#define BUFF_MAX 32 16#define PARK_EAST 1 17#define PARK_WEST 2 18#define PARK_NORTH 3 19#define PARK_SOUTH 4 20#define PARK_FLAT 5 21 22#define MOTOR_DWELL 100 23 24#define MAX_MODBUS_DATA 60 25 26#define HT16K33_DSP_NOBLINK 0 // constants for the half arsed cheapo display 27#define HT16K33_DSP_BLINK1HZ 4 28#define HT16K33_DSP_BLINK2HZ 2 29#define HT16K33_DSP_BLINK05HZ 6 30 31 32const byte ENCODER_PINA = 2; // encoder connects - interupt pin 33const byte ENCODER_PINB = 3; // non interupt pin 34const byte ENCODER_PB = 8; 35 36const byte RELAY_XZ_DIR = 4; // DIR 1 X+ X- North / South Was the X+ N relay 37const byte RELAY_XZ_PWM = 5; // PWM 1 Speed North / South Was the X- S relay 38const byte RELAY_YZ_PWM = 6; // PWM 2 Speed East / West Was the Y+ W relay 39const byte RELAY_YZ_DIR = 7; // DIR 2 Y+ Y- East / West Was the Y- E relay 40 41const byte UNUSED09 = 9; // cycle timer 26 Hz 38ms period 42const byte UNUSED10 = 10; 43const byte UNUSED11 = 11; 44const byte WATCHDOG = 12; 45 46 47//L3G gyro; 48LSM303 compass; 49LiquidCrystal_I2C lcd(0x27); // Set the LCD I2C address I modified the lib for default wiring 50 51//SFE_BMP180 pressure; 52HT16K33 HT; 53 54Modbus slave1(ID, 1, 0); // this is slave ID and RS-232 or USB-FTDI 55 56uint8_t time[8]; 57int motor_recycle = 0 ; 58char recv[BUFF_MAX]; 59unsigned int recv_size = 0; 60unsigned long prev_millis; 61uint8_t u8state; //!< machine state 62uint8_t u8query; //!< pointer to message query 63 64struct ts t; // MODBUS MAP 65struct ts tc; // 44 66int iSave = 0 ; // 43 67int iDoSave = 0 ; // 42 68float T; // 40 temperature of board 69float xzTarget ; // 38 target for angles 70float yzTarget ; // 36 71float xzH ; // 34 hyserisis zone 72float yzH ; // 32 73float xzAng; // 30 current angles 74float yzAng; // 28 75float xzOffset; // 26 offset xz 76float yzOffset; // 24 offset yz 77float dyPark; // 22 parking position 78float dxPark; // 20 79float xMinVal ; // 18 Min and Max values X - N/S 80float xMaxVal ; // 16 81float yMinVal ; // 14 Y -- E/W 82float yMaxVal ; // 12 83float latitude; // 10 84float longitude; // 8 85int timezone; // 7 86int iDayNight ; // 6 87float solar_az_deg; // 4 88float solar_el_deg; // 2 89int iTrackMode ; // 1 90int iMode ; // 0 91 92int iCycle ; 93int iPMode; 94int iPWM_YZ ; 95int iPWM_XZ ; 96 97unsigned long tempus; 98int8_t state1 = 0; 99 100void StopYZ(){ 101 iPWM_YZ=0 ; 102 motor_recycle = MOTOR_DWELL ; 103} 104void StopXZ(){ 105 iPWM_XZ=0 ; 106 motor_recycle = MOTOR_DWELL ; 107} 108 109void ActivateRelays(int iAllStop) { 110 if (motor_recycle > 0 ){ 111 motor_recycle-- ; 112 } 113 if ( iAllStop == 0 ) { 114 StopYZ() ; 115 StopXZ() ; 116 } else { 117 if (( iPWM_YZ==0 ) && (motor_recycle == 0 )){ 118 if (((yzAng ) < ( yzTarget - yzH )) ) { // do Y ie E/W before N/S 119 digitalWrite(RELAY_YZ_DIR, LOW) ; 120 iPWM_YZ=128 ; 121 } 122 if (((yzAng ) > (yzTarget + yzH )) ) { 123 digitalWrite(RELAY_YZ_DIR, HIGH) ; 124 iPWM_YZ=128 ; 125 } 126 } 127 if ( iPWM_YZ>0 ){ 128 if ((yzAng > yzTarget) && ( digitalRead(RELAY_YZ_DIR)==LOW )) { 129 StopYZ() ; 130 } 131 if ((yzAng < yzTarget) && ( digitalRead(RELAY_YZ_DIR)==HIGH )) { 132 StopYZ() ; 133 } 134 } 135 136 if (( iPWM_YZ==0)) { // if finished on E/W you can do N/S 137 if (( iPWM_XZ==0 ) && (motor_recycle == 0 )){ 138 if ((xzAng < ( xzTarget - xzH )) ) { // turn on if not in tolerance 139 digitalWrite(RELAY_XZ_DIR, LOW) ; 140 iPWM_XZ=128 ; 141 } 142 if ((xzAng > ( xzTarget + xzH )) ) { // turn on if not in tolerance 143 digitalWrite(RELAY_XZ_DIR, HIGH) ; 144 iPWM_XZ=128 ; 145 } 146 } 147 }else{ 148 if ((iPWM_XZ>0 )){ 149 StopXZ() ; 150 } 151 } 152 if ( iPWM_XZ>0 ){ 153 if ((xzAng > xzTarget ) && ( digitalRead(RELAY_XZ_DIR)==LOW )) { // if on turn off 154 StopXZ() ; 155 } 156 if ((xzAng < xzTarget ) && ( digitalRead(RELAY_XZ_DIR)==HIGH )) { // if on turn off 157 StopXZ() ; 158 } 159 } 160 } 161 if (iPWM_XZ>0){ 162 iPWM_XZ += 3 ; 163 } 164 if (iPWM_YZ>0){ 165 iPWM_YZ += 3 ; 166 } 167 iPWM_XZ = constrain(iPWM_XZ,0,254); 168 iPWM_YZ = constrain(iPWM_YZ,0,254); 169 analogWrite(RELAY_XZ_PWM,iPWM_XZ); 170 analogWrite(RELAY_YZ_PWM,iPWM_YZ); 171} 172 173void FloatToModbusWords(float src_value , uint16_t * dest_lo , uint16_t * dest_hi ) { 174 uint16_t tempdata[2] ; 175 float *tf ; 176 tf = (float * )&tempdata[0] ; 177 *tf = src_value ; 178 *dest_lo = tempdata[1] ; 179 *dest_hi = tempdata[0] ; 180} 181float FloatFromModbusWords( uint16_t dest_lo , uint16_t dest_hi ) { 182 uint16_t tempdata[2] ; 183 float *tf ; 184 tf = (float * )&tempdata[0] ; 185 tempdata[1] = dest_lo ; 186 tempdata[0] = dest_hi ; 187 return (*tf) ; 188} 189 190// Arduino doesnt have these to we define from a sandard libruary 191float arcsin(float x) { 192 return (atan(x / sqrt(-x * x + 1))); 193} 194float arccos(float x) { 195 return (atan(x / sqrt(-x * x + 1)) + (2 * atan(1))); 196} 197// fractional orbital rotation in radians 198float gama(struct ts *tm) { 199 return ((2 * PI / 365 ) * DayOfYear(tm->year , tm->mon , tm->mday , tm->hour , tm->min )); 200} 201// equation of rime 202float eqTime(float g) { 203 return (229.18 * ( 0.000075 + ( 0.001868 * cos(g)) - (0.032077 * sin(g)) - (0.014615 * cos (2 * g)) - (0.040849 * sin(2 * g)))); 204} 205// declination of sun in radians 206float Decl(float g) { 207 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)) ); 208} 209float TimeOffset(float longitude , struct ts *tm , int timezone ) { 210 float dTmp ; 211 dTmp = (-4.0 * longitude ) + (60 * timezone) - eqTime(gama(tm)) ; 212 return (dTmp); 213} 214 215float TrueSolarTime(float longitude , struct ts *tm , int timezone ) { 216 float dTmp ; 217 dTmp = ( 60.0 * tm->hour ) + (1.0 * tm->min) + (1.0 * tm->sec / 60) - TimeOffset(longitude, tm, timezone) ; 218 return (dTmp); 219} 220float HourAngle(float longitude , struct ts *tm , int timezone) { 221 float dTmp; 222 dTmp = (TrueSolarTime(longitude, tm, timezone) / 4 ) - 180 ; // 720 minutes is solar noon -- div 4 is 180 223 return (dTmp); 224} 225// Hour angle for sunrise and sunset only 226float HA (float lat , struct ts *tm ) { 227 float latRad ; 228 latRad = lat * 2 * PI / 360 ; 229 return ( acos((cos(90.833 * PI / 180 ) / ( cos(latRad) * cos(Decl(gama(tm)))) - (tan(latRad) * tan(Decl(gama(tm)))))) / PI * 180 ); 230} 231 232float Sunrise(float longitude , float lat , struct ts *tm , int timezone) { 233 return (720 - ( 4.0 * (longitude + HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 234} 235float Sunset(float longitude , float lat , struct ts *tm , int timezone) { 236 return (720 - ( 4.0 * (longitude - HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 237} 238float SNoon(float longitude , float lat , struct ts *tm , int timezone) { 239 return (720 - ( 4.0 * (longitude + (60 * timezone) - eqTime(gama(tm)))) ) ; 240} 241 242float SolarZenithRad(float longitude , float lat , struct ts *tm , int timezone) { 243 float latRad ; 244 float decRad ; 245 float HourAngleRad ; 246 float dTmp ; 247 248 latRad = lat * 2 * PI / 360 ; 249 decRad = Decl(gama(tm)); 250 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 251 dTmp = acos((sin(latRad) * sin(decRad)) + (cos(latRad) * cos(decRad) * cos(HourAngleRad))); 252 return (dTmp) ; 253 254} 255float SolarElevationRad(float longitude , float lat , struct ts *tm , int timezone ) { 256 return ((PI / 2) - SolarZenithRad(longitude , lat , tm , timezone )) ; 257} 258 259float SolarAzimouthRad(float longitude , float lat , struct ts *tm , int timezone) { 260 float latRad ; 261 float decRad ; 262 float solarzenRad ; 263 float HourAngleRad ; 264 float dTmp ; 265 latRad = lat * 2 * PI / 360 ; 266 decRad = Decl(gama(tm)); 267 solarzenRad = SolarZenithRad ( longitude , lat , tm , timezone ) ; 268 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 269 dTmp = acos(((sin(decRad) * cos(latRad)) - (cos(HourAngleRad) * cos(decRad) * sin(latRad))) / sin(solarzenRad)) ; 270 if ( HourAngleRad < 0 ) { 271 return (dTmp) ; 272 } else { 273 return ((2 * PI) - dTmp) ; 274 } 275} 276 277float LoadFloatFromEEPROM(int address,float minval,float maxval, float defaultval){ 278float tmp ; 279 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 280 if (( tmp < minval ) || ( tmp > maxval )|| (NumberOK(tmp) == 1)) { 281 tmp = defaultval ; 282 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 283 } 284 return(tmp); 285} 286int LoadIntFromEEPROM(int address,int minval,int maxval, int defaultval){ 287int tmp ; 288 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 289 if (( tmp < minval ) || ( tmp > maxval )) { 290 tmp = defaultval ; 291 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 292 } 293 return(tmp); 294} 295 296int NumberOK (float target) { 297 int tmp = 0 ; 298 tmp = isnan(target); 299 if ( tmp != 1 ) { 300 tmp = isinf(target); 301 } 302 return (tmp); 303} 304 305void IncFloat(float * target, boolean bInc, float increment, float minval, float maxval , boolean bWrap){ 306 if( bInc ){ 307 *target += increment ; 308 }else{ 309 *target -= increment ; 310 } 311 if ( *target > maxval ){ 312 if ( bWrap ){ 313 *target = minval ; 314 }else{ 315 *target = maxval ; 316 } 317 } 318 if ( *target < minval ){ 319 if ( bWrap ){ 320 *target = maxval ; 321 }else{ 322 *target = minval ; 323 } 324 } 325} 326void IncInt(int * target, boolean bInc, int increment, int minval, int maxval , boolean bWrap){ 327 if( bInc ){ 328 *target += increment ; 329 }else{ 330 *target -= increment ; 331 } 332 if ( *target > maxval ){ 333 if ( bWrap ){ 334 *target = minval ; 335 }else{ 336 *target = maxval ; 337 } 338 } 339 if ( *target < minval ){ 340 if ( bWrap ){ 341 *target = maxval ; 342 }else{ 343 *target = minval ; 344 } 345 } 346} 347void IncUint8_t (uint8_t * target , boolean bInc, int increment, int minval, int maxval , boolean bWrap){ 348 if( bInc ){ 349 *target += increment ; 350 }else{ 351 *target -= increment ; 352 } 353 if ( *target > maxval ){ 354 if ( bWrap ){ 355 *target = minval ; 356 }else{ 357 *target = maxval ; 358 } 359 } 360 if ( *target < minval ){ 361 if ( bWrap ){ 362 *target = maxval ; 363 }else{ 364 *target = minval ; 365 } 366 } 367} 368void StdFlloatValueDisplay(float target, char message[]){ 369 lcd.setCursor ( 0, 1 ); // line 1 370 lcd.print(message) ; 371 lcd.print(target) ; 372 lcd.setCursor ( 0, 2 ); // line 2 373 lcd.print( "PB to set ") ; 374} 375 376 377void setup() { 378 int led ; 379 380 Wire.begin(); 381 lcd.begin(20, 4); 382 lcd.home(); 383 lcd.setBacklightPin(3, NEGATIVE); 384 lcd.noCursor(); 385 386 MCUSR &= ~_BV(WDRF); 387 wdt_disable(); 388 389 compass.init(); 390 compass.enableDefault(); 391 compass.setTimeout(1000); 392 393 pinMode(RELAY_XZ_DIR, OUTPUT); // Outputs for PWM motor control 394 pinMode(RELAY_XZ_PWM, OUTPUT); // 395 pinMode(RELAY_YZ_PWM, OUTPUT); // 396 pinMode(RELAY_YZ_DIR, OUTPUT); // 397 iPWM_YZ = 0 ; 398 iPWM_XZ = 0 ; 399 pinMode(13, OUTPUT); // 400 digitalWrite(13, HIGH ); 401 ActivateRelays(0); // call an all stop first 402 403 Serial.begin(9600) ; // use as a diagnostic port 404 slave1.begin( 9600 ); // RS-232 to base of tower 405 tempus = millis() + 100; 406 407 pinMode(ENCODER_PINA, INPUT_PULLUP); 408 pinMode(ENCODER_PINB, INPUT_PULLUP); 409 pinMode(ENCODER_PB, INPUT_PULLUP); 410 attachInterrupt(0, counter, FALLING); 411 412 pinMode(UNUSED09, OUTPUT); // unused so dont leave floating set as output 413 pinMode(UNUSED10, OUTPUT); // 414 pinMode(UNUSED11, OUTPUT); // 415 pinMode(WATCHDOG, OUTPUT); // 416 digitalWrite(WATCHDOG,HIGH); 417 418 compass.m_min = (LSM303::vector<int16_t>) {-3848, -1822, -1551 }; // calibration figures are empirical 419 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300}; 420 421 xzH = LoadFloatFromEEPROM(0,0.1,20.0,4.0); // hysterisis NS 422 yzH = LoadFloatFromEEPROM(1,0.1,20.0,4.0); // "" EW 423 424 dyPark = LoadFloatFromEEPROM(2,-70.0,50.0,0); 425 dxPark = LoadFloatFromEEPROM(3,-5.0,50.0,0.0); 426 427 xzOffset = LoadFloatFromEEPROM(4,-90.0,90.0,0); // NS 428 yzOffset = LoadFloatFromEEPROM(5,-90.0,90.0,0); // EW 429 430 xzTarget = LoadFloatFromEEPROM(6,-90.0,90.0,0); // NS 431 yzTarget = LoadFloatFromEEPROM(7,-90.0,90.0,0); // EW 432 433 xMinVal = LoadFloatFromEEPROM(8,-10.0,60.0,0.0); // NS 434 xMaxVal = LoadFloatFromEEPROM(9,-10.0,60.0,45); 435 436 yMinVal = LoadFloatFromEEPROM(10,-70.0,50.0,-65); // EW 437 yMaxVal = LoadFloatFromEEPROM(11,-70.0,50.0,45); 438 439 iMode = 0 ; 440 iTrackMode = LoadIntFromEEPROM(12,-1,4,0); 441 442// latitude = -34.051219 ; 443// longitude = 142.013618 ; 444// timezone = 10 ; 445 446 latitude = LoadFloatFromEEPROM(13,-90.0,90.0,-34.051219); 447 longitude = LoadFloatFromEEPROM(14,-180.0,180.0,142.013618); 448 timezone = LoadIntFromEEPROM(15,0,23,10); 449 450 DS3231_init(DS3231_INTCN); 451 DS3231_get(&tc); 452 DS3231_get(&t); 453 lcd.clear(); 454 HT.begin(0x00); 455 for (led = 0; led < 127; led++) { 456 HT.clearLedNow(led); 457 } 458 wdt_enable(WDTO_4S); 459} 460 461void loop() { 462 float P; 463 float sunInc; 464 float sunAng; 465 float xzRatio; 466 float yzRatio; 467 char buff[BUFF_MAX]; 468 float decl ; 469 float eqtime ; 470 float dTmp ; 471 float ha ; 472 float heading ; 473 float sunrise ; 474 float sunset ; 475 float tst ; 476 float sunX ; 477 478 compass.read(); // this reads all 6 channels 479// compass.readAcc(); 480// compass.readMag(); 481 digitalWrite(UNUSED09,!digitalRead(UNUSED09)); // toggle this output so I can measure the cycle time with a scope 482 483 T = DS3231_get_treg(); 484 heading = compass.heading((LSM303::vector<int>) { 1, 0, 0 }); 485 486 if (( compass.a.z != 0) && (!compass.timeoutOccurred() )) { 487 xzRatio = (float)compass.a.y / abs((float)compass.a.z) ; // yep if your sharp you can see I swapped axis orientation late in the programming 488 yzRatio = (float)compass.a.x / abs((float)compass.a.z) ; 489 xzAng = ((float)atan(xzRatio) / PI * -180 ) + xzOffset ; // good old offsets or fudge factors 490 yzAng = ((float)atan(yzRatio) / PI * 180 ) + yzOffset ; 491 digitalWrite(13, LOW); 492 }else{ // try restarting the compass/accelerometer modual - cos he gone walkabout... 493 Wire.begin(); // reset the I2C 494 compass.init(); 495 compass.enableDefault(); 496 compass.setTimeout(1000); // BTW I fixed up the int / long issue in the time out function in the LM303 lib I was using 497 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 !!) 498 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300 }; 499 if (tc.sec % 2 == 0 ) { 500 digitalWrite(13, HIGH); 501 } else { 502 digitalWrite(13, LOW); 503 } 504 HT.begin(0x00); 505 } 506 507 DS3231_get(&tc); 508 509 solar_az_deg = SolarAzimouthRad(longitude, latitude, &tc, timezone) * 180 / PI ; 510 solar_el_deg = SolarElevationRad(longitude, latitude, &tc, timezone) * 180 / PI ; 511 512 decl = Decl(gama(&tc)) * 180 / PI ; 513 ha = HourAngle (longitude , &tc , timezone ) ; 514 sunrise = Sunrise(longitude, latitude, &tc, timezone) ; 515 sunset = Sunset(longitude, latitude, &tc, timezone); 516 tst = TrueSolarTime(longitude, &tc, timezone); 517 sunX = abs(latitude) + decl ; 518 if (solar_el_deg >= 0 ){ // day 519 iDayNight = 1 ; 520 }else{ // night 521 iDayNight = 0 ; 522 } 523 524 switch (iTrackMode) { 525 case 4: // both axis to park 526 yzTarget = dyPark ; // night park position E/W 527 xzTarget = dxPark ; // night park position N/S 528 break ; 529 case 3: // both axis off no tracking 530 break ; 531 case 2: // xz tracking NS 532 if ( iDayNight == 1 ) { 533 xzTarget = sunX ; // need to map the coordinate system correctly 534 } else { 535 xzTarget = dxPark ; // night park position 536 } 537 break; 538 case 1: // yz tracking EW 539 if (iDayNight == 1) { 540 yzTarget = ha ; 541 } else { 542 yzTarget = dyPark ; // night park position 543 } 544 break; 545 case -1: // set target to tracking and park both at nigh 546 if (iDayNight == 1) { 547 yzTarget = ha ; 548 xzTarget = sunX ; // need to map the coordinate system correctly 549 } else { 550 yzTarget = dyPark ; // night park position E/W 551 xzTarget = dxPark ; // night park position N/S 552 } 553 break; 554 default: // set target to tracking 555 if (iDayNight == 1) { 556 yzTarget = ha ; 557 xzTarget = sunX ; // need to map the coordinate system correctly 558 } else { 559 yzTarget = dyPark ; // night park position (dont park the other - leave till morning) 560 } 561 break; 562 } 563 xzTarget = constrain(xzTarget,xMinVal,xMaxVal); // constain function... very cool - dont leave home without it ! 564 yzTarget = constrain(yzTarget,yMinVal,yMaxVal); 565 566 if ( iPMode != iMode ) { // clear screen after first change of mode 567 lcd.clear(); 568 if ((( iPMode < 1 ) || ( iPMode > 8 )) && ( iMode > 0 ) && (iMode < 9 )) { 569 DS3231_get(&t); // when going into time edit mode update the time - then leave it allone to be worked on by the editor 570 } 571 iPMode = iMode ; 572 } 573 if ( iCycle != tc.sec ) { //only update once a second 574 digitalWrite(WATCHDOG,LOW); // external watch dog pin 575 wdt_reset(); // reset internal watchdog - good puppy 576 // lcd.clear(); 577 if ( iMode > 0 ){ 578 lcd.setCursor ( 0, 3 ); // line 3 579 lcd.print( "Mode ") ; 580 lcd.print(iMode) ; 581 lcd.print( " " ) ; 582 switch (tc.sec % 2 ){ 583 case 1: 584 lcd.print( "|" ) ; 585 break; 586 default: 587 lcd.print( "-" ) ; 588 break; 589 } 590 } 591 lcd.setCursor ( 0, 0 ); // line 0 592 switch (iMode) { 593 case 22: 594 StdFlloatValueDisplay(yMaxVal,"Y Max "); 595 break; 596 case 21: 597 StdFlloatValueDisplay(yMinVal,"Y Min "); 598 break; 599 case 20: 600 StdFlloatValueDisplay(xMaxVal,"X Max "); 601 break; 602 case 19: 603 StdFlloatValueDisplay(xMinVal,"X Min "); 604 break; 605 case 23: 606 lcd.setCursor ( 0, 0 ); 607 lcd.print("Lat "); 608 lcd.print(latitude,6); 609 lcd.setCursor ( 0, 1 ); 610 lcd.print("Long "); 611 lcd.print(longitude,6); 612 lcd.setCursor ( 0, 2 ); 613 lcd.print("Time Zone "); 614 lcd.print(timezone); 615 break; 616 case 24: 617 // lcd.clear(); 618 lcd.setCursor ( 0, 0 ); 619 lcd.print("X/Z "); 620 if ( xzAng > 0 ) { 621 lcd.print("+"); 622 } 623 lcd.print(xzAng); 624 lcd.setCursor ( 10, 0 ); 625 lcd.print("Y/Z "); 626 if ( yzAng > 0 ) { 627 lcd.print("+"); 628 } 629 lcd.print(yzAng); 630 lcd.setCursor ( 0, 1 ); 631 lcd.print("TX "); 632 if (( xzTarget) > 0 ) { 633 lcd.print("+"); 634 } 635 lcd.print(( xzTarget)); 636 lcd.setCursor ( 10, 1 ); 637 lcd.print("TY "); 638 if (( yzTarget) > 0 ) { 639 lcd.print("+"); 640 } 641 lcd.print(( yzTarget)); 642 lcd.setCursor ( 0, 2 ); 643 lcd.print("DX "); 644 dTmp = ( xzAng - xzTarget) ; 645 if (dTmp > 0 ) { 646 lcd.print("+"); 647 } 648 lcd.print(dTmp); 649 lcd.setCursor ( 10, 2 ); 650 lcd.print("DY "); 651 dTmp = ( yzAng - yzTarget) ; 652 if (dTmp > 0 ) { 653 lcd.print("+"); 654 } 655 lcd.print(dTmp); 656 657 lcd.setCursor ( 10, 3 ); // line 2 658 lcd.print( "T ") ; 659 lcd.print(T) ; 660 661 lcd.setCursor ( 18, 3 ); // line 2 662 if ( iPWM_YZ == 0 ) { 663 lcd.print( " ") ; 664 }else{ 665 if (( digitalRead(RELAY_YZ_DIR) == LOW )) { 666 lcd.print( "W") ; 667 }else{ 668 lcd.print( "E") ; 669 } 670 } 671 lcd.setCursor ( 19, 3 ); // line 2 672 if ( iPWM_XZ == 0 ) { 673 lcd.print( " ") ; 674 }else{ 675 if (( digitalRead(RELAY_XZ_DIR) == LOW )) { 676 lcd.print( "N") ; 677 }else{ 678 lcd.print( "S") ; 679 } 680 } 681 682 break; 683 case 18: // SAVE ALL 684 lcd.setCursor ( 0, 1 ); // line 1 685 if ( iSave != 0 ) { 686 lcd.print( "SAVE REQUIRED") ; 687 } else { 688 lcd.print( "### SAVED ###") ; 689 } 690 if ( iDoSave == 2 ) { 691 EEPROM.put( 0 , xzH ); 692 EEPROM.put(0 + (1 * sizeof(float)) , yzH ); 693 EEPROM.put(0 + (2 * sizeof(float)) , dyPark ); 694 EEPROM.put(0 + (3 * sizeof(float)) , dxPark ); 695 EEPROM.put(0 + (4 * sizeof(float)) , xzOffset ); 696 EEPROM.put(0 + (5 * sizeof(float)) , yzOffset ); 697 EEPROM.put(0 + (6 * sizeof(float)) , xzTarget ); 698 EEPROM.put(0 + (7 * sizeof(float)) , yzTarget ); 699 EEPROM.put(0 + (8 * sizeof(float)) , xMinVal ); 700 EEPROM.put(0 + (9 * sizeof(float)) , xMaxVal ); 701 EEPROM.put(0 + (10 * sizeof(float)) , yMinVal ); 702 EEPROM.put(0 + (11 * sizeof(float)) , yMaxVal ); 703 EEPROM.put(0 + (12 * sizeof(float)) , iTrackMode ); 704 EEPROM.put(0 + (13 * sizeof(float)) , latitude ); 705 EEPROM.put(0 + (14 * sizeof(float)) , longitude ); 706 EEPROM.put(0 + (15 * sizeof(float)) , timezone ); 707 iDoSave = 0 ; 708 iSave = 0 ; 709 } 710 lcd.setCursor ( 0, 2 ); // line 2 711 lcd.print( "PB to Save ALL") ; 712 break ; 713 case 17: // yzH hysterisis 714 StdFlloatValueDisplay(yzH,"Y Hysteresis "); 715 break; 716 case 16: // xzH hysterisis 717 StdFlloatValueDisplay(xzH,"X Hysteresis "); 718 break; 719 case 15: // yOffset 720 StdFlloatValueDisplay(yzOffset,"Y Offset "); 721 break; 722 case 14: // xOffset 723 StdFlloatValueDisplay(xzOffset,"X Offset "); 724 break; 725 case 13: 726 lcd.setCursor ( 0, 1 ); // line 1 727 lcd.print(iTrackMode) ; 728 lcd.print( " - ") ; 729 PrintTrackerMode(iTrackMode); 730 lcd.setCursor ( 0, 2 ); // line 2 731 lcd.print( "PB change track mode") ; 732 break; 733 case 12: 734 StdFlloatValueDisplay(dxPark,"X Park "); 735 736 break; 737 case 11: 738 StdFlloatValueDisplay(dyPark,"Y Park "); 739 740 break; 741 case 10: 742 StdFlloatValueDisplay(yzTarget,"Y "); 743 lcd.print( " WE angle") ; 744 break; 745 case 9: 746 StdFlloatValueDisplay(xzTarget,"X "); 747 lcd.print( " NS angle") ; 748 break; 749 case 8: 750 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 751 lcd.print(buff) ; 752 lcd.setCursor ( 0, 1 ); // line 2 753 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 754 lcd.print(buff) ; 755 lcd.setCursor ( 0, 2 ); // line 2 756 lcd.print( "Press PB to set time ") ; 757 if ( not digitalRead(ENCODER_PB) ) { 758 DS3231_set(t); 759 } 760 break; 761 case 1: 762 case 2: 763 case 3: 764 case 4: 765 case 5: 766 case 6: 767 case 7: 768 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 769 lcd.print(buff) ; 770 if (iMode == 7) { 771 lcd.setCursor ( 0, 1 ); 772 lcd.print( "Day - > ") ; 773 PrintDay(t.wday); 774 } 775 lcd.setCursor ( 0, 2 ); 776 lcd.print( "Hold in set") ; 777 lcd.setCursor ( 10, 3 ) ; 778 switch (iMode) { 779 case 1: 780 lcd.print( "Year") ; 781 break; 782 case 2: 783 lcd.print( "Month") ; 784 break; 785 case 3: 786 lcd.print( "Day") ; 787 break; 788 case 4: 789 lcd.print( "Hour") ; 790 break; 791 case 5: 792 lcd.print( "Min") ; 793 break; 794 case 6: 795 lcd.print( "Sec") ; 796 break; 797 case 7: 798 lcd.print( "WDay") ; 799 break; 800 } 801 // lcd.noBlink(); 802 break; 803 default: // 0 804 DS3231_get(&tc); // update the time registers 805 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 806 lcd.print(buff) ; 807 lcd.setCursor ( 0, 1 ); // line 2 has the Az and El 808 lcd.print( "Az ") ; 809 lcd.print(solar_az_deg) ; 810 lcd.setCursor ( 10, 1 ); 811 lcd.print( "El ") ; 812 lcd.print(solar_el_deg) ; 813 814 lcd.setCursor ( 0, 2 ); 815 lcd.print("D "); 816 if ( decl > 0 ) { 817 lcd.print("+"); 818 } 819 lcd.print(decl); 820 lcd.setCursor ( 10, 2 ); 821 lcd.print("H "); 822 if ( ha > 0 ) { 823 lcd.print("+"); 824 } 825 lcd.print(ha); 826 lcd.setCursor ( 0, 3 ); // third line is sunset and sunrise 827 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunrise), MinSolarTime(sunrise)); 828 lcd.print(buff) ; 829 if (iDayNight == 1) { 830 lcd.setCursor ( 8, 3 ); 831 lcd.print(" DAY ") ; 832 } else { 833 lcd.setCursor ( 7, 3 ); 834 lcd.print("NIGHT") ; 835 } 836 lcd.setCursor ( 15, 3 ); 837 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunset), MinSolarTime(sunset)); 838 lcd.print(buff) ; 839 break; 840 } 841 842 iCycle = tc.sec ; 843 DisplayMeatBall() ; 844 } 845 846 if (((tc.hour > 19 ) || ( tc.hour < 5 )) && (iTrackMode < 3)) { 847 ActivateRelays(0) ; // power down at night if in tracking mode 848 }else{ 849 ActivateRelays(1) ; 850 } 851 digitalWrite(WATCHDOG,HIGH); // nice doggy 852 853 state1 = slave1.poll( (uint16_t*)&iMode, MAX_MODBUS_DATA ); 854 855 switch (state1) { 856 case EXC_ADDR_RANGE: 857 // Serial.println("EXC_ADDR_RANGE PORT 1"); 858 break; 859 case EXC_FUNC_CODE: 860 // Serial.println("EXC_FUNC_CODE PORT 1"); 861 break; 862 case EXC_REGS_QUANT: 863 // Serial.println("EXC_REGS_QUANT PORT 1"); 864 break; 865 } 866 867} 868 869 870 871 872 873 874float DayOfYear(uint16_t iYear , uint8_t iMon , uint8_t iDay , uint8_t iHour , uint8_t iMin ) { 875 int i ; 876 float iTDay ; 877 878 iTDay = iDay - 1 ; // this is zero referenced 879 for ( i = 1 ; i < iMon ; i++ ) { 880 switch (i) { 881 case 1: 882 case 3: 883 case 5: 884 case 7: 885 case 8: 886 case 10: 887 case 12: 888 iTDay += 31 ; 889 break; 890 case 4: 891 case 6: 892 case 9: 893 case 11: 894 iTDay += 30 ; 895 break; 896 case 2 : 897 if ((iYear % 4) == 0 ) { 898 iTDay += 29 ; 899 } else { 900 iTDay += 28 ; 901 } 902 break; 903 } 904 } 905 iTDay += (( 1.0 * iHour - 12 ) / 24 ) ; 906 // iDay += 1.0 * iMin / 1440 ; 907 return (iTDay); 908} 909 910void PrintTrackerMode(int iTarget) { 911 switch (iTarget) { 912 case 1: 913 lcd.print( "EW Only") ; 914 break; 915 case 2: 916 lcd.print( "NS Only") ; 917 break; 918 case 3: 919 lcd.print( "None") ; 920 break; 921 case 4: 922 lcd.print( "Both Park") ; 923 break; 924 case -1: 925 lcd.print( "2P ") ; 926 default: 927 lcd.print( "Both Track") ; 928 break; 929 } 930} 931 932 933void PrintDay(int iTarget) { 934 switch (iTarget) { 935 case 1: 936 lcd.print( "Sun") ; 937 break; 938 case 2: 939 lcd.print( "Mon") ; 940 break; 941 case 3: 942 lcd.print( "Tue") ; 943 break; 944 case 4: 945 lcd.print( "Wed") ; 946 break; 947 case 5: 948 lcd.print( "Thr") ; 949 break; 950 case 6: 951 lcd.print( "Fri") ; 952 break; 953 case 7: 954 lcd.print( "Sat") ; 955 break; 956 } 957} 958 959int HrsSolarTime(float target) { 960 int i ; 961 i = target ; 962 return ( i / 60 ); 963} 964int MinSolarTime(float target) { 965 int i ; 966 i = target ; 967 return ( i % 60 ); 968} 969 970void counter() 971{ 972 973 if ( ( prev_millis + 100 ) < millis() ) { 974 if ( digitalRead(ENCODER_PB) ) { 975 IncInt(&iMode,digitalRead(ENCODER_PINB),1,0,24,true); 976 } else { 977 if ((iMode > 8 ) && (iMode < 23) && (iMode != 18)) { 978 iSave = 1; 979 } 980 switch (iMode) { // setting the time and date etc 981 case 1: // year 982 IncInt(&t.year ,digitalRead(ENCODER_PINB),1,2016,3116,false); 983 break; 984 case 2: // month 985 IncUint8_t(&t.mon ,digitalRead(ENCODER_PINB),1,1,12,true); 986 break; 987 case 3: // day 988 IncUint8_t(&t.mday ,digitalRead(ENCODER_PINB),1,1,31,true); 989 break; 990 case 4: // hour 991 IncUint8_t(&t.hour ,digitalRead(ENCODER_PINB),1,0,23,true); 992 break; 993 case 5: 994 IncUint8_t(&t.min ,digitalRead(ENCODER_PINB),1,0,59,true); 995 break; 996 case 6: 997 IncUint8_t(&t.sec ,digitalRead(ENCODER_PINB),1,0,59,true); 998 break; 999 case 7: 1000 IncUint8_t(&t.wday ,digitalRead(ENCODER_PINB),1,1,7,true); 1001 break; 1002 case 9: 1003 IncFloat(&xzTarget,digitalRead(ENCODER_PINB),1,0,90,false); 1004 break; 1005 case 10: 1006 IncFloat(&yzTarget,digitalRead(ENCODER_PINB),1,-80,80,false); 1007 break; 1008 case 11: 1009 IncFloat(&dyPark,digitalRead(ENCODER_PINB),1,-80,80,false); 1010 break; 1011 case 12: 1012 IncFloat(&dxPark,digitalRead(ENCODER_PINB),1,-80,80,false); 1013 break; 1014 case 13: 1015 IncInt(&iTrackMode,digitalRead(ENCODER_PINB),1,-1,4,true); 1016 break; 1017 case 14: 1018 IncFloat(&xzOffset,digitalRead(ENCODER_PINB),0.5,-20,20,false); 1019 break; 1020 case 15: 1021 IncFloat(&yzOffset,digitalRead(ENCODER_PINB),0.5,-20,20,false); 1022 break; 1023 case 16: 1024 IncFloat(&xzH,digitalRead(ENCODER_PINB),0.25,-20,20,false); 1025 break; 1026 case 17: 1027 IncFloat(&yzH,digitalRead(ENCODER_PINB),0.25,-20,20,false); 1028 break; 1029 case 18: 1030 if (( iDoSave == 0 ) && ( iSave != 0 )) { 1031 iDoSave = 2 ; 1032 } 1033 break; 1034 case 19: 1035 IncFloat(&xMinVal,digitalRead(ENCODER_PINB),1.0,-10,50,false); 1036 break; 1037 case 20: 1038 IncFloat(&xMaxVal,digitalRead(ENCODER_PINB),1.0,-10,50,false); 1039 break; 1040 case 21: 1041 IncFloat(&yMinVal,digitalRead(ENCODER_PINB),1.0,-70,50,false); 1042 break; 1043 case 22: 1044 IncFloat(&yMaxVal,digitalRead(ENCODER_PINB),1.0,-70,50,false); 1045 break; 1046 } 1047 } 1048 prev_millis = millis(); 1049 } else { 1050 if ( prev_millis > ( millis() + 1000 ) ) { 1051 prev_millis = millis(); 1052 } 1053 } 1054 1055} 1056 1057float sign(float target) { 1058 if (target > 0 ) { 1059 return (1); 1060 } else { 1061 if (target < 0 ) { 1062 return (-1); 1063 } else { 1064 return (0); 1065 } 1066 } 1067} 1068 1069void DisplayMeatBall() { 1070 int pos , led , x , y; 1071 float dx , dy ; 1072 float dxa , dya ; 1073 1074 HT.setBrightness(15); 1075 1076 dx = xzAng - xzTarget ; 1077 dy = yzAng - yzTarget ; 1078 dxa = abs(dx) ; 1079 dya = abs(dy) ; 1080 if (dxa < 6) { 1081 x = 0 ; 1082 } else { 1083 if (dxa < 12) { 1084 x = sign(dx); 1085 } else { 1086 if (dxa < 50) { 1087 x = 2 * sign(dx); 1088 } else { 1089 x = 3 * sign(dx); 1090 } 1091 } 1092 } 1093 if (dya < 6) { 1094 y = 0 ; 1095 } else { 1096 if (dya < 12) { 1097 y = sign(dy); 1098 } else { 1099 if (dya < 25) { 1100 y = 2 * sign(dy); 1101 } else { 1102 y = 3 * sign(dy); 1103 } 1104 } 1105 } 1106 pos = 27 ; // netral position 1107 pos += (y * 8) ; // add or sumtract the x in range of -3 to +3 1108 pos += (x ) ; // add or sumtract 8 * y or y in range of -3 to +3 1109 for (led = 0; led < 63; led++) { 1110 switch (led){ 1111 case 0: 1112 case 7: 1113 case 56: 1114 case 63: 1115 break; 1116 default: 1117 HT.clearLedNow(MapLedNo(led)); 1118 break; 1119 } 1120 } 1121 1122 HT.setLedNow(MapLedNo(0)); // turn on four courners 1123 HT.setLedNow(MapLedNo(7)); 1124 HT.setLedNow(MapLedNo(56)); 1125 HT.setLedNow(MapLedNo(63)); 1126 1127 1128 // HT.setLedNow(MapLedNo(tc.sec)); 1129 if ((iPWM_YZ == 0) && (iPWM_XZ == 0)) { 1130 HT.setBlinkRate(HT16K33_DSP_NOBLINK); // not attempting to move 1131 if (tc.sec % 2 == 0 ) { 1132 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1133 HT.setLedNow(MapLedNo(pos + 9)); 1134 }else{ 1135 HT.setLedNow(MapLedNo(pos + 1)); 1136 HT.setLedNow(MapLedNo(pos + 8)); 1137 } 1138 } else { 1139 HT.setBlinkRate(HT16K33_DSP_BLINK2HZ); //moving so blink meat ball 1140 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1141 HT.setLedNow(MapLedNo(pos + 1)); 1142 HT.setLedNow(MapLedNo(pos + 8)); 1143 HT.setLedNow(MapLedNo(pos + 9)); 1144 } 1145} 1146 1147 1148int MapLedNo(int target) // this compensates for the screwy setup of the matrix driver to the chip 1149{ 1150 int row ; 1151 int col ; 1152 row = target / 8 ; 1153 col = target % 8 ; 1154 if (col == 0 ) { 1155 return ((row * 16 ) + 7) ; 1156 } else { 1157 return ((row * 16 ) + col - 1) ; 1158 } 1159} 1160 1161
DS3231 lib
c_cpp
This is an older copy of this lib that works with this project
1inary file (no preview
modified LM303 lib
c_cpp
very small changes aimed at roll over detection of the millis function which is used to detect time outs
1inary file (no preview
Modified I2C library
c_cpp
Been asked a few times for my tweaked library files.. Small changes to suit LCD's and boards in my "kit" Thanks to the original author... From this you should find the rest...
1// --------------------------------------------------------------------------- 2// 3 Created by Francisco Malpartida on 20/08/11. 4// Copyright 2011 - Under creative 5 commons license 3.0: 6// Attribution-ShareAlike CC BY-SA 7// 8// This 9 software is furnished "as is", without technical support, and with no 10// warranty, 11 express or implied, as to its usefulness for any purpose. 12// 13// Thread Safe: 14 No 15// Extendable: Yes 16// 17// @file LiquidCrystal_I2C.c 18// This file implements 19 a basic liquid crystal library that comes as standard 20// in the Arduino SDK but 21 using an I2C IO extension board. 22// 23// @brief 24// This is a basic implementation 25 of the LiquidCrystal library of the 26// Arduino SDK. The original library has 27 been reworked in such a way that 28// this class implements the all methods to 29 command an LCD based 30// on the Hitachi HD44780 and compatible chipsets using 31 I2C extension 32// backpacks such as the I2CLCDextraIO with the PCF8574* I2C IO 33 Expander ASIC. 34// 35// The functionality provided by this class and its base 36 class is identical 37// to the original functionality of the Arduino LiquidCrystal 38 library. 39// 40// 41// 42// @author F. Malpartida - fmalpartida@gmail.com 43// 44 --------------------------------------------------------------------------- 45#if 46 (ARDUINO < 100) 47#include <WProgram.h> 48#else 49#include <Arduino.h> 50#endif 51#include 52 <inttypes.h> 53#include "I2CIO.h" 54#include "LiquidCrystal_I2C.h" 55 56// 57 CONSTANT definitions 58// --------------------------------------------------------------------------- 59 60// 61 flags for backlight control 62/*! 63 @defined 64 @abstract LCD_NOBACKLIGHT 65 66 @discussion NO BACKLIGHT MASK 67 */ 68#define LCD_NOBACKLIGHT 0x00 69 70/*! 71 72 @defined 73 @abstract LCD_BACKLIGHT 74 @discussion BACKLIGHT MASK used when 75 backlight is on 76 */ 77#define LCD_BACKLIGHT 0xFF 78 79 80// Default library 81 configuration parameters used by class constructor with 82// only the I2C address 83 field. 84// --------------------------------------------------------------------------- 85/*! 86 87 @defined 88 @abstract Enable bit of the LCD 89 @discussion Defines the IO of 90 the expander connected to the LCD Enable 91 */ 92#define EN 2 // Enable bit 93 94/*! 95 96 @defined 97 @abstract Read/Write bit of the LCD 98 @discussion Defines the 99 IO of the expander connected to the LCD Rw pin 100 */ 101#define RW 1 // Read/Write 102 bit 103 104/*! 105 @defined 106 @abstract Register bit of the LCD 107 @discussion 108 Defines the IO of the expander connected to the LCD Register select pin 109 */ 110#define 111 RS 0 // Register select bit 112 113/*! 114 @defined 115 @abstract LCD dataline 116 allocation this library only supports 4 bit LCD control 117 mode. 118 @discussion 119 D4, D5, D6, D7 LCD data lines pin mapping of the extender module 120 */ 121#define 122 D4 4 123#define D5 5 124#define D6 6 125#define D7 7 126 127 128// CONSTRUCTORS 129// 130 --------------------------------------------------------------------------- 131LiquidCrystal_I2C::LiquidCrystal_I2C( 132 uint8_t lcd_Addr ) 133{ 134 config(lcd_Addr, EN, RW, RS, D4, D5, D6, D7); 135} 136 137 138LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t 139 lcd_Addr, uint8_t backlighPin, 140 t_backlighPol 141 pol = POSITIVE) 142{ 143 config(lcd_Addr, EN, RW, RS, D4, D5, D6, D7); 144 setBacklightPin(backlighPin, 145 pol); 146} 147 148LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t 149 En, uint8_t Rw, 150 uint8_t Rs) 151{ 152 config(lcd_Addr, 153 En, Rw, Rs, D4, D5, D6, D7); 154} 155 156LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t 157 lcd_Addr, uint8_t En, uint8_t Rw, 158 uint8_t 159 Rs, uint8_t backlighPin, 160 t_backlighPol 161 pol = POSITIVE) 162{ 163 config(lcd_Addr, En, Rw, Rs, D4, D5, D6, D7); 164 setBacklightPin(backlighPin, 165 pol); 166} 167 168LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr, uint8_t 169 En, uint8_t Rw, 170 uint8_t Rs, uint8_t d4, 171 uint8_t d5, 172 uint8_t d6, uint8_t d7 ) 173{ 174 175 config(lcd_Addr, En, Rw, Rs, d4, d5, d6, d7); 176} 177 178LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t 179 lcd_Addr, uint8_t En, uint8_t Rw, 180 uint8_t 181 Rs, uint8_t d4, uint8_t d5, 182 uint8_t d6, 183 uint8_t d7, uint8_t backlighPin, 184 t_backlighPol 185 pol = POSITIVE ) 186{ 187 config(lcd_Addr, En, Rw, Rs, d4, d5, d6, d7); 188 setBacklightPin(backlighPin, 189 pol); 190} 191 192// PUBLIC METHODS 193// --------------------------------------------------------------------------- 194 195// 196// 197 begin 198void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) 199 200{ 201 202 init(); // Initialise the I2C expander interface 203 LCD::begin 204 ( cols, lines, dotsize ); 205} 206 207 208// User commands - users can expand 209 this section 210//---------------------------------------------------------------------------- 211// 212 Turn the (optional) backlight off/on 213 214// 215// setBacklightPin 216void LiquidCrystal_I2C::setBacklightPin 217 ( uint8_t value, t_backlighPol pol = POSITIVE ) 218{ 219 _backlightPinMask = ( 220 1 << value ); 221 _polarity = pol; 222 setBacklight(BACKLIGHT_OFF); 223} 224 225// 226// 227 setBacklight 228void LiquidCrystal_I2C::setBacklight( uint8_t value ) 229{ 230 231 // Check if backlight is available 232 // ---------------------------------------------------- 233 234 if ( _backlightPinMask != 0x0 ) 235 { 236 // Check for polarity to configure 237 mask accordingly 238 // ---------------------------------------------------------- 239 240 if (((_polarity == POSITIVE) && (value > 0)) || 241 ((_polarity 242 == NEGATIVE ) && ( value == 0 ))) 243 { 244 _backlightStsMask = _backlightPinMask 245 & LCD_BACKLIGHT; 246 } 247 else 248 { 249 _backlightStsMask 250 = _backlightPinMask & LCD_NOBACKLIGHT; 251 } 252 _i2cio.write( _backlightStsMask 253 ); 254 } 255} 256 257 258// PRIVATE METHODS 259// --------------------------------------------------------------------------- 260 261// 262// 263 init 264int LiquidCrystal_I2C::init() 265{ 266 int status = 0; 267 268 // 269 initialize the backpack IO expander 270 // and display functions. 271 // ------------------------------------------------------------------------ 272 273 if ( _i2cio.begin ( _Addr ) == 1 ) 274 { 275 _i2cio.portMode ( OUTPUT 276 ); // Set the entire IO extender to OUTPUT 277 _displayfunction = LCD_4BITMODE 278 | LCD_1LINE | LCD_5x8DOTS; 279 status = 1; 280 _i2cio.write(0); // Set 281 the entire port to LOW 282 } 283 return ( status ); 284} 285 286// 287// config 288void 289 LiquidCrystal_I2C::config (uint8_t lcd_Addr, uint8_t En, uint8_t Rw, uint8_t Rs, 290 291 uint8_t d4, uint8_t d5, uint8_t d6, uint8_t 292 d7 ) 293{ 294 _Addr = lcd_Addr; 295 296 _backlightPinMask = 0x08; 297 _backlightStsMask 298 = LCD_BACKLIGHT; 299 _polarity = POSITIVE; 300 301 _En = ( 1 << En ); 302 303 _Rw = ( 1 << Rw ); 304 _Rs = ( 1 << Rs ); 305 306 // Initialise pin mapping 307 308 _data_pins[0] = ( 1 << d4 ); 309 _data_pins[1] = ( 1 << d5 ); 310 _data_pins[2] 311 = ( 1 << d6 ); 312 _data_pins[3] = ( 1 << d7 ); 313} 314 315 316 317// low 318 level data pushing commands 319//---------------------------------------------------------------------------- 320 321// 322// 323 send - write either command or data 324void LiquidCrystal_I2C::send(uint8_t value, 325 uint8_t mode) 326{ 327 // No need to use the delay routines since the time taken 328 to write takes 329 // longer that what is needed both for toggling and enable 330 pin an to execute 331 // the command. 332 333 if ( mode == FOUR_BITS ) 334 335 { 336 write4bits( (value & 0x0F), COMMAND ); 337 } 338 else 339 { 340 341 write4bits( (value >> 4), mode ); 342 write4bits( (value & 0x0F), mode); 343 344 } 345 delay(1); 346} 347 348// 349// write4bits 350void LiquidCrystal_I2C::write4bits 351 ( uint8_t value, uint8_t mode ) 352{ 353 uint8_t pinMapValue = 0; 354 355 356 // Map the value to LCD pin mapping 357 // -------------------------------- 358 359 for ( uint8_t i = 0; i < 4; i++ ) 360 { 361 if ( ( value & 0x1 ) == 1 362 ) 363 { 364 pinMapValue |= _data_pins[i]; 365 } 366 value 367 = ( value >> 1 ); 368 } 369 370 // Is it a command or data 371 // ----------------------- 372 373 if ( mode == DATA ) 374 { 375 mode = _Rs; 376 } 377 378 pinMapValue 379 |= mode | _backlightStsMask; 380 pulseEnable ( pinMapValue ); 381} 382 383// 384// 385 pulseEnable 386void LiquidCrystal_I2C::pulseEnable (uint8_t data) 387{ 388 _i2cio.write 389 (data | _En); // En HIGH 390 _i2cio.write (data & ~_En); // En LOW 391}
Solar_Calcs.bas
vbscript
Early proof of concept code....
1Attribute VB_Name = "SolarStuff" 2Option Explicit 3 4Global gdblLat 5 As Double 6Global gdblLong As Double 7 8Global Const PI = 3.14159265358979 9 10Function 11 arccos(x) 12 arccos = Atn(-x / Sqr(-x * x + 1)) + 2 * Atn(1) 13End Function 14 15Function 16 arcsin(x) 17 arcsin = Atn(x / Sqr(-x * x + 1)) 18End Function 19 20' 21' 22 Fraction of a year 23' 24Function gama(Optional MyDate) As Double 25Dim dtNow 26 As Date 27Dim dt1st As Date 28 If IsMissing(MyDate) Then 29 MyDate 30 = Now() 31 Else 32 MyDate = CDate(MyDate) 33 End If 34 dt1st 35 = CDate("1/1/" & Year(MyDate)) 36 gama = (2 * PI / 365) * (DateDiff("y", 37 dt1st, MyDate) + ((Hour(MyDate) - 12) / 24)) 38' gama = (2 * PI / 364) * ((DateDiff("y", 39 dt1st, MyDate) + ((hour(MyDate) - 12) * 60 + Minute(MyDate)) / 1440)) 40' gama 41 = (2 * PI / 365) * (DateDiff("y", dt1st, MyDate) + ((hour(MyDate)) / 24)) 42End 43 Function 44' 45' 46' Equation of time 47' In Minutes 48Function eqTime(g As 49 Double) As Double 50 eqTime = 229.18 * (0.000075 + (0.001868 * Cos(g)) - (0.032077 51 * Sin(g)) - (0.014615 * Cos(2 * g)) - (0.040849 * Sin(2 * g))) 52 53' 54 eqTime = (229.18 * (0.000075 + 0.001568 * Cos(g) - 0.032077 * Sin(g) - 0.014615 55 * Cos(2 * g) - 0.040849 * Sin(2 * g))) 56 57End Function 58' 59' 60' Declination 61 of the sun 62' In Radians 63Function Decl(g As Double) As Double 64 Decl 65 = 0.006918 - (0.399912 * Cos(g)) + (0.070257 * Sin(g)) - (0.006758 * Cos(2 * g)) 66 + (0.000907 * Sin(2 * g)) - (0.002697 * Cos(3 * g)) + (0.00148 * Sin(3 * g)) 67' 68 Decl = (0.006918 - 0.399912 * Cos(g) + 0.070257 * Sin(g) - 0.006758 * Cos(2 69 * g) + 0.000907 * Sin(2 * g)) 70End Function 71' 72' 73' Eq time in minutes 74' 75Function 76 time_offset(Longitude As Double, MyTime As Date, TimeZone As Long) As Double 77 78 time_offset = (-4 * Longitude) + (60 * TimeZone) - eqTime(gama(MyTime)) 79End 80 Function 81 82' 83' tst 84' In minutes 85' 86Function TrueSolarTime(Longitude 87 As Double, MyTime As Date, TimeZone As Long) As Double 88 TrueSolarTime = (Hour(MyTime) 89 * 60) + Minute(MyTime) + (Second(MyTime) / 60) - time_offset(Longitude, MyTime, 90 TimeZone) 91End Function 92' 93' 94' in degrees 95' -180 to 180 96Function 97 hourAngle(Longitude As Double, MyTime As Date, TimeZone As Long) As Double 98 hourAngle 99 = (TrueSolarTime(Longitude, MyTime, TimeZone) / 4) - 180 ' 720 minutes is solar 100 noon -- div 4 gives 180 101End Function 102 103 104' Hour Angle at sunset or sunrise 105 only !!!! 106' in degrees (ie solar zenith is 90.833 deg) 107Function ha(lat As 108 Double, MyDate As Date) As Double 109 Dim latRad As Double 110 latRad = lat 111 * 2 * PI / 360 112 ha = arccos((Cos(90.833 * PI / 180) / (Cos(latRad) * Cos(Decl(gama(MyDate)))) 113 - (Tan(latRad) * Tan(Decl(gama(MyDate)))))) / PI * 180 114End Function 115 116 117Function 118 Sunrise(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As 119 Double 120 Sunrise = 720 - (4 * (Longitude + ha(lat, MyTime))) + (60 * TimeZone) 121 - eqTime(gama(MyTime)) 122End Function 123Function SunriseTime(Longitude As Double, 124 lat As Double, MyTime As Date, TimeZone As Long) As Date 125Dim dblTmp As Double 126 127 dblTmp = Sunrise(Longitude, lat, MyTime, TimeZone) 128 SunriseTime = DateAdd("n", 129 CInt(dblTmp), DateSerial(Year(MyTime), Month(MyTime), Day(MyTime))) 130End Function 131 132Function 133 Sunset(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As 134 Double 135 Sunset = 720 - (4 * (Longitude - ha(lat, MyTime))) + (60 * TimeZone) 136 - eqTime(gama(MyTime)) 137End Function 138Function SunsetTime(Longitude As Double, 139 lat As Double, MyTime As Date, TimeZone As Long) As Date 140Dim dblTmp As Double 141 142 dblTmp = Sunset(Longitude, lat, MyTime, TimeZone) 143 SunsetTime = DateAdd("n", 144 CInt(dblTmp), DateSerial(Year(MyTime), Month(MyTime), Day(MyTime))) 145End Function 146 147Function 148 Snoon(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 149 150 Snoon = 720 - (4 * Longitude) + (60 * TimeZone) - eqTime(gama(MyTime)) 151End 152 Function 153Function SnoonTime(Longitude As Double, lat As Double, MyTime As Date, 154 TimeZone As Long) As Date 155Dim dblTmp As Double 156 dblTmp = Snoon(Longitude, 157 lat, MyTime, TimeZone) 158 SnoonTime = DateAdd("n", CInt(dblTmp), DateSerial(Year(MyTime), 159 Month(MyTime), Day(MyTime))) 160End Function 161 162Function SolarZenithRad(Longitude 163 As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 164Dim dTmp 165 As Double 166Dim latRad As Double 167Dim decrad As Double 168Dim HourAngleRad As 169 Double 170 171 latRad = lat * 2 * PI / 360 172 decrad = Decl(gama(MyTime)) 173 174 HourAngleRad = hourAngle(Longitude, MyTime, TimeZone) * PI / 180 175 176 177 dTmp = arccos((Sin(latRad) * Sin(decrad)) + (Cos(latRad) * Cos(decrad) * Cos(HourAngleRad))) 178 179 SolarZenithRad = dTmp 180End Function 181 182Function SolarElevationRad(Longitude 183 As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 184Dim dTmp 185 As Double 186Dim latRad As Double 187Dim decrad As Double 188Dim HourAngleRad As 189 Double 190 191 latRad = lat * 2 * PI / 360 192 decrad = Decl(gama(MyTime)) 193 194 HourAngleRad = hourAngle(Longitude, MyTime, TimeZone) * PI / 180 195 196 197 dTmp = arccos((Sin(latRad) * Sin(decrad)) + (Cos(latRad) * Cos(decrad) * Cos(HourAngleRad))) 198 199 SolarElevationRad = (PI / 2) - dTmp 200End Function 201 202 203Function SolarAzimuthRad(Longitude 204 As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 205Dim dTmp 206 As Double 207Dim latRad As Double 208Dim decrad As Double 209Dim solzenrad As Double 210Dim 211 HourAngleRad As Double 212 213 latRad = lat * 2 * PI / 360 214 decrad = Decl(gama(MyTime)) 215 216 solzenrad = SolarZenithRad(Longitude, lat, MyTime, TimeZone) 217 HourAngleRad 218 = hourAngle(Longitude, MyTime, TimeZone) * PI / 180 219 220' dTmp = arccos(-1 221 * (Cos(solzenrad) * Sin(latrad) - Sin(decrad)) / (Sin(solzenrad) * Cos(latrad))) 222' 223 dTmp = arccos((Sin(decrad) - (Sin(decrad) * Sin(latrad))) / (Sin(solzenrad) 224 * Cos(latrad))) 225 dTmp = arccos(((Sin(decrad) * Cos(latRad)) - (Cos(HourAngleRad) 226 * Cos(decrad) * Sin(latRad))) / Sin(solzenrad)) 227 If HourAngleRad < 0 Then 228 229 SolarAzimuthRad = dTmp 230 Else 231 SolarAzimuthRad = (2 * PI) 232 - dTmp 233 End If 234 235End Function 236 237' 238' ???????? nor working 239 ????? from winkipedia 240' 241Function SolarDeclinationRad(MyDate As Date) As Double 242Dim 243 dTmp As Double 244Dim N As Double 245 246 N = DateDiff("y", CDate("1/1/" 247 & CStr(Year(MyDate))), MyDate) + (((Hour(MyDate) - 12) * 60 + Minute(MyDate)) / 248 1440) ' days since 1/1 in year 249 dTmp = -arcsin(0.39779 * Cos(0.0172029 * (N 250 + 10) + 0.0334 * Sin(0.0172029 * (N - 2)))) ' constants converted to radians (was 251 degrees in winkipedia) 252 SolarDeclinationRad = dTmp ' * 180 / PI ' converter 253 to degrees 254End Function 255 256 257Function JulianDay(MyDate As Date) As Double 258 259End 260 Function 261Function JulianCentury(MyDate As Date) As Double 262 263End Function 264 265
TRACKER_ALL_IN_ONE_BOTTOM.ino
c_cpp
This for a "Uno" Board using the described hardware There are 24 ish screens of data and diagnostics .... uses the chips internal eeprom for non-volatile storage. Good coding exercise as I has do be more efficient as the program grew...It been a long time since I have had only 32K to program in . Don't stress about the modified lib's - they are only patches to faults in the coding. If you need just ask....
1//#include <SFE_BMP180.h> 2#include <avr/wdt.h> 3#include <Wire.h> 4#include <LSM303.h> // modified ... fixed a couple of bugs 5#include <LiquidCrystal_I2C.h> 6//#include <L3G.h> 7#include "ds3231.h" 8#include "ht16k33.h" 9#include <ModbusRtu.h> // Modified ... this no longer a stock lib - Slave supports register translation/mapping 10#include <EEPROM.h> 11#include <math.h> 12 13#define ID 1 14 15#define BUFF_MAX 32 16#define PARK_EAST 1 17#define PARK_WEST 2 18#define PARK_NORTH 3 19#define PARK_SOUTH 4 20#define PARK_FLAT 5 21 22 23#define MAX_MODBUS_DATA 60 24 25#define HT16K33_DSP_NOBLINK 0 // constants for the half arsed cheapo display 26#define HT16K33_DSP_BLINK1HZ 4 27#define HT16K33_DSP_BLINK2HZ 2 28#define HT16K33_DSP_BLINK05HZ 6 29 30 31const byte ENCODER_PINA = 2; // encoder connects - interupt pin 32const byte ENCODER_PINB = 3; // non interupt pin 33const byte ENCODER_PB = 8; 34 35const byte RELAY_XZ_F = 4; // X+ Relay 1 North 36const byte RELAY_XZ_R = 5; // X- Relay 2 South 37const byte RELAY_YZ_F = 6; // Y+ Relay 3 West 38const byte RELAY_YZ_R = 7; // Y- Relay 4 East 39 40const byte UNUSED09 = 9; 41const byte UNUSED10 = 10; 42const byte UNUSED11 = 11; 43const byte WATCHDOG = 12; 44 45 46//L3G gyro; 47LSM303 compass; 48LiquidCrystal_I2C lcd(0x27); // Set the LCD I2C address I modified the lib for default wiring 49 50//SFE_BMP180 pressure; 51HT16K33 HT; 52 53Modbus slave1(ID, 1, 0); // this is slave ID and RS-232 or USB-FTDI 54 55uint8_t time[8]; 56 57char recv[BUFF_MAX]; 58unsigned int recv_size = 0; 59unsigned long prev_millis; 60uint8_t u8state; //!< machine state 61uint8_t u8query; //!< pointer to message query 62 63struct ts t; // MODBUS MAP 64struct ts tc; // 44 65int iSave = 0 ; // 43 66int iDoSave = 0 ; // 42 67float T; // 40 temperature of board 68float xzTarget ; // 38 target for angles 69float yzTarget ; // 36 70float xzH ; // 34 hyserisis zone 71float yzH ; // 32 72float xzAng; // 30 current angles 73float yzAng; // 28 74float xzOffset; // 26 offset xz 75float yzOffset; // 24 offset yz 76float dyPark; // 22 parking position 77float dxPark; // 20 78float xMinVal ; // 18 Min and Max values X - N/S 79float xMaxVal ; // 16 80float yMinVal ; // 14 Y -- E/W 81float yMaxVal ; // 12 82float latitude; // 10 83float longitude; // 8 84int timezone; // 7 85int iDayNight ; // 6 86float solar_az_deg; // 4 87float solar_el_deg; // 2 88int iTrackMode ; // 1 89int iMode ; // 0 90 91int iCycle ; 92int iPMode; 93 94float baseline; // baseline pressure 95float gT ; 96unsigned long tempus; 97int8_t state1 = 0; 98 99 100void ActivateRelays(int iAllStop) { 101 if ( iAllStop == 0 ) { 102 digitalWrite(RELAY_YZ_R, HIGH) ; 103 digitalWrite(RELAY_YZ_F, HIGH) ; 104 digitalWrite(RELAY_XZ_R, HIGH) ; 105 digitalWrite(RELAY_XZ_F, HIGH) ; 106 } else { 107 if ((tc.sec % 5) == 0 ){ 108 if (((yzAng ) < ( yzTarget - yzH )) && ( digitalRead(RELAY_YZ_F) == HIGH )) { // do Y ie E/W before N/S 109 digitalWrite(RELAY_YZ_F, LOW) ; 110 digitalWrite(RELAY_YZ_R, HIGH) ; 111 } 112 if (((yzAng ) > (yzTarget + yzH )) && ( digitalRead(RELAY_YZ_R) == HIGH )) { 113 digitalWrite(RELAY_YZ_F, HIGH) ; 114 digitalWrite(RELAY_YZ_R, LOW) ; 115 } 116 } 117 if ((yzAng > yzTarget) && ( digitalRead(RELAY_YZ_F) == LOW )) { 118 digitalWrite(RELAY_YZ_F, HIGH) ; 119 digitalWrite(RELAY_YZ_R, HIGH) ; 120 } 121 if ((yzAng < yzTarget) && ( digitalRead(RELAY_YZ_R) == LOW )) { 122 digitalWrite(RELAY_YZ_F, HIGH) ; 123 digitalWrite(RELAY_YZ_R, HIGH) ; 124 } 125 126 if (( digitalRead(RELAY_YZ_F) == HIGH ) && ( digitalRead(RELAY_YZ_R) == HIGH )) { // if finished on E/W you can do N/S 127 if ((tc.sec % 5) == 0 ){ 128 if ((xzAng < ( xzTarget - xzH )) && ( digitalRead(RELAY_XZ_F) == HIGH )) { // turn on if not in tolerance 129 digitalWrite(RELAY_XZ_F, LOW) ; 130 digitalWrite(RELAY_XZ_R, HIGH) ; 131 } 132 if ((xzAng > ( xzTarget + xzH ))&& ( digitalRead(RELAY_XZ_R) == HIGH )) { // turn on if not in tolerance 133 digitalWrite(RELAY_XZ_F, HIGH) ; 134 digitalWrite(RELAY_XZ_R, LOW) ; 135 } 136 } 137 }else{ 138 if ((digitalRead(RELAY_XZ_F) == LOW ) || (digitalRead(RELAY_XZ_R) == LOW )){ 139 digitalWrite(RELAY_XZ_F, HIGH) ; // switch off if on 140 digitalWrite(RELAY_XZ_R, HIGH) ; 141 } 142 } 143 if ((xzAng > xzTarget ) && ( digitalRead(RELAY_XZ_F) == LOW )) { // if on turn off 144 digitalWrite(RELAY_XZ_F, HIGH) ; 145 digitalWrite(RELAY_XZ_R, HIGH) ; 146 } 147 if ((xzAng < xzTarget ) && ( digitalRead(RELAY_XZ_R) == LOW )) { // if on turn off 148 digitalWrite(RELAY_XZ_F, HIGH) ; 149 digitalWrite(RELAY_XZ_R, HIGH) ; 150 } 151 } 152} 153 154void FloatToModbusWords(float src_value , uint16_t * dest_lo , uint16_t * dest_hi ) { 155 uint16_t tempdata[2] ; 156 float *tf ; 157 tf = (float * )&tempdata[0] ; 158 *tf = src_value ; 159 *dest_lo = tempdata[1] ; 160 *dest_hi = tempdata[0] ; 161} 162float FloatFromModbusWords( uint16_t dest_lo , uint16_t dest_hi ) { 163 uint16_t tempdata[2] ; 164 float *tf ; 165 tf = (float * )&tempdata[0] ; 166 tempdata[1] = dest_lo ; 167 tempdata[0] = dest_hi ; 168 return (*tf) ; 169} 170 171// Arduino doesnt have these to we define from a sandard libruary 172float arcsin(float x) { 173 return (atan(x / sqrt(-x * x + 1))); 174} 175float arccos(float x) { 176 return (atan(x / sqrt(-x * x + 1)) + (2 * atan(1))); 177} 178// fractional orbital rotation in radians 179float gama(struct ts *tm) { 180 return ((2 * PI / 365 ) * DayOfYear(tm->year , tm->mon , tm->mday , tm->hour , tm->min )); 181} 182// equation of rime 183float eqTime(float g) { 184 return (229.18 * ( 0.000075 + ( 0.001868 * cos(g)) - (0.032077 * sin(g)) - (0.014615 * cos (2 * g)) - (0.040849 * sin(2 * g)))); 185} 186// declination of sun in radians 187float Decl(float g) { 188 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)) ); 189} 190float TimeOffset(float longitude , struct ts *tm , int timezone ) { 191 float dTmp ; 192 dTmp = (-4.0 * longitude ) + (60 * timezone) - eqTime(gama(tm)) ; 193 return (dTmp); 194} 195 196float TrueSolarTime(float longitude , struct ts *tm , int timezone ) { 197 float dTmp ; 198 dTmp = ( 60.0 * tm->hour ) + (1.0 * tm->min) + (1.0 * tm->sec / 60) - TimeOffset(longitude, tm, timezone) ; 199 return (dTmp); 200} 201float HourAngle(float longitude , struct ts *tm , int timezone) { 202 float dTmp; 203 dTmp = (TrueSolarTime(longitude, tm, timezone) / 4 ) - 180 ; // 720 minutes is solar noon -- div 4 is 180 204 return (dTmp); 205} 206// Hour angle for sunrise and sunset only 207float HA (float lat , struct ts *tm ) { 208 float latRad ; 209 latRad = lat * 2 * PI / 360 ; 210 return ( acos((cos(90.833 * PI / 180 ) / ( cos(latRad) * cos(Decl(gama(tm)))) - (tan(latRad) * tan(Decl(gama(tm)))))) / PI * 180 ); 211} 212 213float Sunrise(float longitude , float lat , struct ts *tm , int timezone) { 214 return (720 - ( 4.0 * (longitude + HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 215} 216float Sunset(float longitude , float lat , struct ts *tm , int timezone) { 217 return (720 - ( 4.0 * (longitude - HA(lat, tm))) + (60 * timezone) - eqTime(gama(tm)) ) ; 218} 219float SNoon(float longitude , float lat , struct ts *tm , int timezone) { 220 return (720 - ( 4.0 * (longitude + (60 * timezone) - eqTime(gama(tm)))) ) ; 221} 222 223float SolarZenithRad(float longitude , float lat , struct ts *tm , int timezone) { 224 float latRad ; 225 float decRad ; 226 float HourAngleRad ; 227 float dTmp ; 228 229 latRad = lat * 2 * PI / 360 ; 230 decRad = Decl(gama(tm)); 231 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 232 dTmp = acos((sin(latRad) * sin(decRad)) + (cos(latRad) * cos(decRad) * cos(HourAngleRad))); 233 return (dTmp) ; 234 235} 236float SolarElevationRad(float longitude , float lat , struct ts *tm , int timezone ) { 237 return ((PI / 2) - SolarZenithRad(longitude , lat , tm , timezone )) ; 238} 239 240float SolarAzimouthRad(float longitude , float lat , struct ts *tm , int timezone) { 241 float latRad ; 242 float decRad ; 243 float solarzenRad ; 244 float HourAngleRad ; 245 float dTmp ; 246 latRad = lat * 2 * PI / 360 ; 247 decRad = Decl(gama(tm)); 248 solarzenRad = SolarZenithRad ( longitude , lat , tm , timezone ) ; 249 HourAngleRad = HourAngle (longitude , tm , timezone ) * PI / 180 ; 250 dTmp = acos(((sin(decRad) * cos(latRad)) - (cos(HourAngleRad) * cos(decRad) * sin(latRad))) / sin(solarzenRad)) ; 251 if ( HourAngleRad < 0 ) { 252 return (dTmp) ; 253 } else { 254 return ((2 * PI) - dTmp) ; 255 } 256} 257 258float LoadFloatFromEEPROM(int address,float minval,float maxval, float defaultval){ 259float tmp ; 260 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 261 if (( tmp < minval ) || ( tmp > maxval )|| (NumberOK(tmp) == 1)) { 262 tmp = defaultval ; 263 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 264 } 265 return(tmp); 266} 267int LoadIntFromEEPROM(int address,int minval,int maxval, int defaultval){ 268int tmp ; 269 EEPROM.get(0 + (address * sizeof(float)) , tmp ); 270 if (( tmp < minval ) || ( tmp > maxval )) { 271 tmp = defaultval ; 272 EEPROM.put(0 + (address * sizeof(float)) , tmp ); 273 } 274 return(tmp); 275} 276 277int NumberOK (float target) { 278 int tmp = 0 ; 279 tmp = isnan(target); 280 if ( tmp != 1 ) { 281 tmp = isinf(target); 282 } 283 return (tmp); 284} 285 286void IncFloat(float * target, boolean bInc, float increment, float minval, float maxval , boolean bWrap){ 287 if( bInc ){ 288 *target += increment ; 289 }else{ 290 *target -= increment ; 291 } 292 if ( *target > maxval ){ 293 if ( bWrap ){ 294 *target = minval ; 295 }else{ 296 *target = maxval ; 297 } 298 } 299 if ( *target < minval ){ 300 if ( bWrap ){ 301 *target = maxval ; 302 }else{ 303 *target = minval ; 304 } 305 } 306} 307void IncInt(int * target, boolean bInc, int increment, int minval, int maxval , boolean bWrap){ 308 if( bInc ){ 309 *target += increment ; 310 }else{ 311 *target -= increment ; 312 } 313 if ( *target > maxval ){ 314 if ( bWrap ){ 315 *target = minval ; 316 }else{ 317 *target = maxval ; 318 } 319 } 320 if ( *target < minval ){ 321 if ( bWrap ){ 322 *target = maxval ; 323 }else{ 324 *target = minval ; 325 } 326 } 327} 328void IncUint8_t (uint8_t * target , boolean bInc, int increment, int minval, int maxval , boolean bWrap){ 329 if( bInc ){ 330 *target += increment ; 331 }else{ 332 *target -= increment ; 333 } 334 if ( *target > maxval ){ 335 if ( bWrap ){ 336 *target = minval ; 337 }else{ 338 *target = maxval ; 339 } 340 } 341 if ( *target < minval ){ 342 if ( bWrap ){ 343 *target = maxval ; 344 }else{ 345 *target = minval ; 346 } 347 } 348} 349void StdFlloatValueDisplay(float target, char message[]){ 350 lcd.setCursor ( 0, 1 ); // line 1 351 lcd.print(message) ; 352 lcd.print(target) ; 353 lcd.setCursor ( 0, 2 ); // line 2 354 lcd.print( "PB to set ") ; 355} 356 357 358void setup() { 359 int led ; 360 361 Wire.begin(); 362 lcd.begin(20, 4); 363 lcd.home(); 364 lcd.setBacklightPin(3, NEGATIVE); 365 lcd.noCursor(); 366 367 MCUSR &= ~_BV(WDRF); 368 wdt_disable(); 369 370 compass.init(); 371 compass.enableDefault(); 372 compass.setTimeout(1000); 373 374 pinMode(RELAY_XZ_F, OUTPUT); // relay 375 pinMode(RELAY_XZ_R, OUTPUT); // relay 376 pinMode(RELAY_YZ_F, OUTPUT); // relay 377 pinMode(RELAY_YZ_R, OUTPUT); // relay 378 pinMode(13, OUTPUT); // 379 digitalWrite(13, HIGH ); 380 ActivateRelays(0); // call an all stop first 381 382 Serial.begin(9600) ; // use as a diagnostic port 383 slave1.begin( 9600 ); // RS-232 to base of tower 384 tempus = millis() + 100; 385 386 pinMode(ENCODER_PINA, INPUT_PULLUP); 387 pinMode(ENCODER_PINB, INPUT_PULLUP); 388 pinMode(ENCODER_PB, INPUT_PULLUP); 389 attachInterrupt(0, counter, FALLING); 390 391 pinMode(UNUSED09, OUTPUT); // unused so dont leave floating set as output 392 pinMode(UNUSED10, OUTPUT); // 393 pinMode(UNUSED11, OUTPUT); // 394 pinMode(WATCHDOG, OUTPUT); // 395 digitalWrite(WATCHDOG,HIGH); 396 397 compass.m_min = (LSM303::vector<int16_t>) {-3848, -1822, -1551 }; // calibration figures are empirical 398 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300}; 399 400 xzH = LoadFloatFromEEPROM(0,0.1,20.0,4.0); // hysterisis NS 401 yzH = LoadFloatFromEEPROM(1,0.1,20.0,4.0); // "" EW 402 403 dyPark = LoadFloatFromEEPROM(2,-70.0,50.0,0); 404 dxPark = LoadFloatFromEEPROM(3,-5.0,50.0,0.0); 405 406 xzOffset = LoadFloatFromEEPROM(4,-90.0,90.0,0); // NS 407 yzOffset = LoadFloatFromEEPROM(5,-90.0,90.0,0); // EW 408 409 xzTarget = LoadFloatFromEEPROM(6,-90.0,90.0,0); // NS 410 yzTarget = LoadFloatFromEEPROM(7,-90.0,90.0,0); // EW 411 412 xMinVal = LoadFloatFromEEPROM(8,-10.0,60.0,0.0); // NS 413 xMaxVal = LoadFloatFromEEPROM(9,-10.0,60.0,45); 414 415 yMinVal = LoadFloatFromEEPROM(10,-70.0,50.0,-65); // EW 416 yMaxVal = LoadFloatFromEEPROM(11,-70.0,50.0,45); 417 418 iMode = 0 ; 419 iTrackMode = LoadIntFromEEPROM(12,-1,4,0); 420 421// latitude = -34.051219 ; 422// longitude = 142.013618 ; 423// timezone = 10 ; 424 425 latitude = LoadFloatFromEEPROM(13,-90.0,90.0,-34.051219); 426 longitude = LoadFloatFromEEPROM(14,-180.0,180.0,142.013618); 427 timezone = LoadIntFromEEPROM(15,0,23,10); 428 429 DS3231_init(DS3231_INTCN); 430 DS3231_get(&tc); 431 DS3231_get(&t); 432 lcd.clear(); 433 HT.begin(0x00); 434 for (led = 0; led < 127; led++) { 435 HT.clearLedNow(led); 436 } 437 wdt_enable(WDTO_4S); 438} 439 440void loop() { 441 float P; 442 float sunInc; 443 float sunAng; 444 float xzRatio; 445 float yzRatio; 446 char buff[BUFF_MAX]; 447 float decl ; 448 float eqtime ; 449 float dTmp ; 450 float ha ; 451 float heading ; 452 float sunrise ; 453 float sunset ; 454 float tst ; 455 float sunX ; 456 457 compass.read(); // this reads all 6 channels 458// compass.readAcc(); 459// compass.readMag(); 460 digitalWrite(UNUSED09,!digitalRead(UNUSED09)); // toggle this output so I can measure the cycle time with a scope 461 462 T = DS3231_get_treg(); 463 heading = compass.heading((LSM303::vector<int>) { 1, 0, 0 }); 464 465 if (( compass.a.z != 0) && (!compass.timeoutOccurred() )) { 466 xzRatio = (float)compass.a.y / abs((float)compass.a.z) ; // yep if your sharp you can see I swapped axis orientation late in the programming 467 yzRatio = (float)compass.a.x / abs((float)compass.a.z) ; 468 xzAng = ((float)atan(xzRatio) / PI * -180 ) + xzOffset ; // good old offsets or fudge factors 469 yzAng = ((float)atan(yzRatio) / PI * 180 ) + yzOffset ; 470 }else{ // try restarting the compass/accelerometer modual - cos he gone walkabout... 471 compass.init(); 472 compass.enableDefault(); 473 compass.setTimeout(1000); // BTW I fixed up the int / long issue in the time out function in the LM303 lib I was using 474 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 !!) 475 compass.m_max = (LSM303::vector<int16_t>) { +3353, +5127, +5300 }; 476 } 477 478 DS3231_get(&tc); 479 480 solar_az_deg = SolarAzimouthRad(longitude, latitude, &tc, timezone) * 180 / PI ; 481 solar_el_deg = SolarElevationRad(longitude, latitude, &tc, timezone) * 180 / PI ; 482 483 decl = Decl(gama(&tc)) * 180 / PI ; 484 ha = HourAngle (longitude , &tc , timezone ) ; 485 sunrise = Sunrise(longitude, latitude, &tc, timezone) ; 486 sunset = Sunset(longitude, latitude, &tc, timezone); 487 tst = TrueSolarTime(longitude, &tc, timezone); 488 sunX = abs(latitude) + decl ; 489 if (solar_el_deg >= 0 ){ // day 490 iDayNight = 1 ; 491 }else{ // night 492 iDayNight = 0 ; 493 } 494 495 switch (iTrackMode) { 496 case 4: // both axis to park 497 yzTarget = dyPark ; // night park position E/W 498 xzTarget = dxPark ; // night park position N/S 499 break ; 500 case 3: // both axis off no tracking 501 break ; 502 case 2: // xz tracking NS 503 if ( iDayNight == 1 ) { 504 xzTarget = sunX ; // need to map the coordinate system correctly 505 } else { 506 xzTarget = dxPark ; // night park position 507 } 508 break; 509 case 1: // yz tracking EW 510 if (iDayNight == 1) { 511 yzTarget = ha ; 512 } else { 513 yzTarget = dyPark ; // night park position 514 } 515 break; 516 case -1: // set target to tracking and park both at nigh 517 if (iDayNight == 1) { 518 yzTarget = ha ; 519 xzTarget = sunX ; // need to map the coordinate system correctly 520 } else { 521 yzTarget = dyPark ; // night park position E/W 522 xzTarget = dxPark ; // night park position N/S 523 } 524 break; 525 default: // set target to tracking 526 if (iDayNight == 1) { 527 yzTarget = ha ; 528 xzTarget = sunX ; // need to map the coordinate system correctly 529 } else { 530 yzTarget = dyPark ; // night park position (dont park the other - leave till morning) 531 } 532 break; 533 } 534 xzTarget = constrain(xzTarget,xMinVal,xMaxVal); // constain function... very cool - dont leave home without it ! 535 yzTarget = constrain(yzTarget,yMinVal,yMaxVal); 536 537 if ( iPMode != iMode ) { // clear screen after first change of mode 538 lcd.clear(); 539 if ((( iPMode < 1 ) || ( iPMode > 8 )) && ( iMode > 0 ) && (iMode < 9 )) { 540 DS3231_get(&t); // when going into time edit mode update the time - then leave it allone to be worked on by the editor 541 } 542 iPMode = iMode ; 543 } 544 if ( iCycle != tc.sec ) { //only update once a second 545 digitalWrite(WATCHDOG,LOW); // external watch dog pin 546 wdt_reset(); // reset internal watchdog - good puppy 547 // lcd.clear(); 548 if ( iMode > 0 ){ 549 lcd.setCursor ( 0, 3 ); // line 3 550 lcd.print( "Mode ") ; 551 lcd.print(iMode) ; 552 lcd.print( " " ) ; 553 switch (tc.sec % 2 ){ 554 case 1: 555 lcd.print( "|" ) ; 556 break; 557 default: 558 lcd.print( "-" ) ; 559 break; 560 } 561 } 562 lcd.setCursor ( 0, 0 ); // line 0 563 switch (iMode) { 564 case 22: 565 StdFlloatValueDisplay(yMaxVal,"Y Max "); 566 break; 567 case 21: 568 StdFlloatValueDisplay(yMinVal,"Y Min "); 569 break; 570 case 20: 571 StdFlloatValueDisplay(xMaxVal,"X Max "); 572 break; 573 case 19: 574 StdFlloatValueDisplay(xMinVal,"X Min "); 575 break; 576 case 23: 577 lcd.setCursor ( 0, 0 ); 578 lcd.print("Lat "); 579 lcd.print(latitude,6); 580 lcd.setCursor ( 0, 1 ); 581 lcd.print("Long "); 582 lcd.print(longitude,6); 583 lcd.setCursor ( 0, 2 ); 584 lcd.print("Time Zone "); 585 lcd.print(timezone); 586 break; 587 case 24: 588 // lcd.clear(); 589 lcd.setCursor ( 0, 0 ); 590 lcd.print("X/Z "); 591 if ( xzAng > 0 ) { 592 lcd.print("+"); 593 } 594 lcd.print(xzAng); 595 lcd.setCursor ( 10, 0 ); 596 lcd.print("Y/Z "); 597 if ( yzAng > 0 ) { 598 lcd.print("+"); 599 } 600 lcd.print(yzAng); 601 lcd.setCursor ( 0, 1 ); 602 lcd.print("TX "); 603 if (( xzTarget) > 0 ) { 604 lcd.print("+"); 605 } 606 lcd.print(( xzTarget)); 607 lcd.setCursor ( 10, 1 ); 608 lcd.print("TY "); 609 if (( yzTarget) > 0 ) { 610 lcd.print("+"); 611 } 612 lcd.print(( yzTarget)); 613 lcd.setCursor ( 0, 2 ); 614 lcd.print("DX "); 615 dTmp = ( xzAng - xzTarget) ; 616 if (dTmp > 0 ) { 617 lcd.print("+"); 618 } 619 lcd.print(dTmp); 620 lcd.setCursor ( 10, 2 ); 621 lcd.print("DY "); 622 dTmp = ( yzAng - yzTarget) ; 623 if (dTmp > 0 ) { 624 lcd.print("+"); 625 } 626 lcd.print(dTmp); 627 628 lcd.setCursor ( 10, 3 ); // line 2 629 lcd.print( "T ") ; 630 lcd.print(T) ; 631 632 lcd.setCursor ( 18, 3 ); // line 2 633 if (( digitalRead(RELAY_YZ_F) == LOW )) { 634 lcd.print( "W") ; 635 }else{ 636 if (( digitalRead(RELAY_YZ_R) == LOW )) { 637 lcd.print( "E") ; 638 }else{ 639 lcd.print( " ") ; 640 } 641 } 642 lcd.setCursor ( 19, 3 ); // line 2 643 if (( digitalRead(RELAY_XZ_F) == LOW )) { 644 lcd.print( "N") ; 645 }else{ 646 if (( digitalRead(RELAY_XZ_R) == LOW )) { 647 lcd.print( "S") ; 648 }else{ 649 lcd.print( " ") ; 650 } 651 } 652 653 break; 654 case 18: // SAVE ALL 655 lcd.setCursor ( 0, 1 ); // line 1 656 if ( iSave != 0 ) { 657 lcd.print( "SAVE REQUIRED") ; 658 } else { 659 lcd.print( "### SAVED ###") ; 660 } 661 if ( iDoSave == 2 ) { 662 EEPROM.put( 0 , xzH ); 663 EEPROM.put(0 + (1 * sizeof(float)) , yzH ); 664 EEPROM.put(0 + (2 * sizeof(float)) , dyPark ); 665 EEPROM.put(0 + (3 * sizeof(float)) , dxPark ); 666 EEPROM.put(0 + (4 * sizeof(float)) , xzOffset ); 667 EEPROM.put(0 + (5 * sizeof(float)) , yzOffset ); 668 EEPROM.put(0 + (6 * sizeof(float)) , xzTarget ); 669 EEPROM.put(0 + (7 * sizeof(float)) , yzTarget ); 670 EEPROM.put(0 + (8 * sizeof(float)) , xMinVal ); 671 EEPROM.put(0 + (9 * sizeof(float)) , xMaxVal ); 672 EEPROM.put(0 + (10 * sizeof(float)) , yMinVal ); 673 EEPROM.put(0 + (11 * sizeof(float)) , yMaxVal ); 674 EEPROM.put(0 + (12 * sizeof(float)) , iTrackMode ); 675 EEPROM.put(0 + (13 * sizeof(float)) , latitude ); 676 EEPROM.put(0 + (14 * sizeof(float)) , longitude ); 677 EEPROM.put(0 + (15 * sizeof(float)) , timezone ); 678 iDoSave = 0 ; 679 iSave = 0 ; 680 } 681 lcd.setCursor ( 0, 2 ); // line 2 682 lcd.print( "PB to Save ALL") ; 683 break ; 684 case 17: // yzH hysterisis 685 StdFlloatValueDisplay(yzH,"Y Hysteresis "); 686 break; 687 case 16: // xzH hysterisis 688 StdFlloatValueDisplay(xzH,"X Hysteresis "); 689 break; 690 case 15: // yOffset 691 StdFlloatValueDisplay(yzOffset,"Y Offset "); 692 break; 693 case 14: // xOffset 694 StdFlloatValueDisplay(xzOffset,"X Offset "); 695 break; 696 case 13: 697 lcd.setCursor ( 0, 1 ); // line 1 698 lcd.print(iTrackMode) ; 699 lcd.print( " - ") ; 700 PrintTrackerMode(iTrackMode); 701 lcd.setCursor ( 0, 2 ); // line 2 702 lcd.print( "PB change track mode") ; 703 break; 704 case 12: 705 StdFlloatValueDisplay(dxPark,"X Park "); 706 707 break; 708 case 11: 709 StdFlloatValueDisplay(dyPark,"Y Park "); 710 711 break; 712 case 10: 713 StdFlloatValueDisplay(yzTarget,"Y "); 714 lcd.print( " WE angle") ; 715 break; 716 case 9: 717 StdFlloatValueDisplay(xzTarget,"X "); 718 lcd.print( " NS angle") ; 719 break; 720 case 8: 721 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 722 lcd.print(buff) ; 723 lcd.setCursor ( 0, 1 ); // line 2 724 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 725 lcd.print(buff) ; 726 lcd.setCursor ( 0, 2 ); // line 2 727 lcd.print( "Press PB to set time ") ; 728 if ( not digitalRead(ENCODER_PB) ) { 729 DS3231_set(t); 730 } 731 break; 732 case 1: 733 case 2: 734 case 3: 735 case 4: 736 case 5: 737 case 6: 738 case 7: 739 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", t.year, t.mon, t.mday , t.hour, t.min, t.sec); 740 lcd.print(buff) ; 741 if (iMode == 7) { 742 lcd.setCursor ( 0, 1 ); 743 lcd.print( "Day - > ") ; 744 PrintDay(t.wday); 745 } 746 lcd.setCursor ( 0, 2 ); 747 lcd.print( "Hold in set") ; 748 lcd.setCursor ( 10, 3 ) ; 749 switch (iMode) { 750 case 1: 751 lcd.print( "Year") ; 752 break; 753 case 2: 754 lcd.print( "Month") ; 755 break; 756 case 3: 757 lcd.print( "Day") ; 758 break; 759 case 4: 760 lcd.print( "Hour") ; 761 break; 762 case 5: 763 lcd.print( "Min") ; 764 break; 765 case 6: 766 lcd.print( "Sec") ; 767 break; 768 case 7: 769 lcd.print( "WDay") ; 770 break; 771 } 772 // lcd.noBlink(); 773 break; 774 default: // 0 775 DS3231_get(&tc); // update the time registers 776 snprintf(buff, BUFF_MAX, "%d/%02d/%02d %02d:%02d:%02d", tc.year, tc.mon, tc.mday , tc.hour, tc.min, tc.sec); 777 lcd.print(buff) ; 778 lcd.setCursor ( 0, 1 ); // line 2 has the Az and El 779 lcd.print( "Az ") ; 780 lcd.print(solar_az_deg) ; 781 lcd.setCursor ( 10, 1 ); 782 lcd.print( "El ") ; 783 lcd.print(solar_el_deg) ; 784 785 lcd.setCursor ( 0, 2 ); 786 lcd.print("D "); 787 if ( decl > 0 ) { 788 lcd.print("+"); 789 } 790 lcd.print(decl); 791 lcd.setCursor ( 10, 2 ); 792 lcd.print("H "); 793 if ( ha > 0 ) { 794 lcd.print("+"); 795 } 796 lcd.print(ha); 797 lcd.setCursor ( 0, 3 ); // third line is sunset and sunrise 798 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunrise), MinSolarTime(sunrise)); 799 lcd.print(buff) ; 800 if (iDayNight == 1) { 801 lcd.setCursor ( 8, 3 ); 802 lcd.print(" DAY ") ; 803 } else { 804 lcd.setCursor ( 7, 3 ); 805 lcd.print("NIGHT") ; 806 } 807 lcd.setCursor ( 15, 3 ); 808 snprintf(buff, BUFF_MAX, "%02d:%02d", HrsSolarTime(sunset), MinSolarTime(sunset)); 809 lcd.print(buff) ; 810 break; 811 } 812 813 iCycle = tc.sec ; 814 DisplayMeatBall() ; 815 if (tc.sec % 2 == 0 ) { 816 digitalWrite(13, HIGH); 817 } else { 818 digitalWrite(13, LOW); 819 } 820 } 821 822 if (((tc.hour > 19 ) || ( tc.hour < 5 )) && (iTrackMode < 3)) { 823 ActivateRelays(0) ; // power down at night if in tracking mode 824 }else{ 825 ActivateRelays(1) ; 826 } 827 digitalWrite(WATCHDOG,HIGH); // nice doggy 828 829 state1 = slave1.poll( (uint16_t*)&iMode, MAX_MODBUS_DATA ); 830 831 switch (state1) { 832 case EXC_ADDR_RANGE: 833 // Serial.println("EXC_ADDR_RANGE PORT 1"); 834 break; 835 case EXC_FUNC_CODE: 836 // Serial.println("EXC_FUNC_CODE PORT 1"); 837 break; 838 case EXC_REGS_QUANT: 839 // Serial.println("EXC_REGS_QUANT PORT 1"); 840 break; 841 } 842 843} 844 845 846 847 848 849 850float DayOfYear(uint16_t iYear , uint8_t iMon , uint8_t iDay , uint8_t iHour , uint8_t iMin ) { 851 int i ; 852 float iTDay ; 853 854 iTDay = iDay - 1 ; // this is zero referenced 855 for ( i = 1 ; i < iMon ; i++ ) { 856 switch (i) { 857 case 1: 858 case 3: 859 case 5: 860 case 7: 861 case 8: 862 case 10: 863 case 12: 864 iTDay += 31 ; 865 break; 866 case 4: 867 case 6: 868 case 9: 869 case 11: 870 iTDay += 30 ; 871 break; 872 case 2 : 873 if ((iYear % 4) == 0 ) { 874 iTDay += 29 ; 875 } else { 876 iTDay += 28 ; 877 } 878 break; 879 } 880 } 881 iTDay += (( 1.0 * iHour - 12 ) / 24 ) ; 882 // iDay += 1.0 * iMin / 1440 ; 883 return (iTDay); 884} 885 886void PrintTrackerMode(int iTarget) { 887 switch (iTarget) { 888 case 1: 889 lcd.print( "EW Only") ; 890 break; 891 case 2: 892 lcd.print( "NS Only") ; 893 break; 894 case 3: 895 lcd.print( "None") ; 896 break; 897 case 4: 898 lcd.print( "Both Park") ; 899 break; 900 case -1: 901 lcd.print( "2P ") ; 902 default: 903 lcd.print( "Both Track") ; 904 break; 905 } 906} 907 908 909void PrintDay(int iTarget) { 910 switch (iTarget) { 911 case 1: 912 lcd.print( "Sun") ; 913 break; 914 case 2: 915 lcd.print( "Mon") ; 916 break; 917 case 3: 918 lcd.print( "Tue") ; 919 break; 920 case 4: 921 lcd.print( "Wed") ; 922 break; 923 case 5: 924 lcd.print( "Thr") ; 925 break; 926 case 6: 927 lcd.print( "Fri") ; 928 break; 929 case 7: 930 lcd.print( "Sat") ; 931 break; 932 } 933} 934 935int HrsSolarTime(float target) { 936 int i ; 937 i = target ; 938 return ( i / 60 ); 939} 940int MinSolarTime(float target) { 941 int i ; 942 i = target ; 943 return ( i % 60 ); 944} 945 946void counter() 947{ 948 949 if ( ( prev_millis + 100 ) < millis() ) { 950 if ( digitalRead(ENCODER_PB) ) { 951 IncInt(&iMode,digitalRead(ENCODER_PINB),1,0,24,true); 952 } else { 953 if ((iMode > 8 ) && (iMode < 23) && (iMode != 18)) { 954 iSave = 1; 955 } 956 switch (iMode) { // setting the time and date etc 957 case 1: // year 958 IncInt(&t.year ,digitalRead(ENCODER_PINB),1,2016,3116,false); 959 break; 960 case 2: // month 961 IncUint8_t(&t.mon ,digitalRead(ENCODER_PINB),1,1,12,true); 962 break; 963 case 3: // day 964 IncUint8_t(&t.mday ,digitalRead(ENCODER_PINB),1,1,31,true); 965 break; 966 case 4: // hour 967 IncUint8_t(&t.hour ,digitalRead(ENCODER_PINB),1,0,23,true); 968 break; 969 case 5: 970 IncUint8_t(&t.min ,digitalRead(ENCODER_PINB),1,0,59,true); 971 break; 972 case 6: 973 IncUint8_t(&t.sec ,digitalRead(ENCODER_PINB),1,0,59,true); 974 break; 975 case 7: 976 IncUint8_t(&t.wday ,digitalRead(ENCODER_PINB),1,1,7,true); 977 break; 978 case 9: 979 IncFloat(&xzTarget,digitalRead(ENCODER_PINB),1,0,90,false); 980 break; 981 case 10: 982 IncFloat(&yzTarget,digitalRead(ENCODER_PINB),1,-80,80,false); 983 break; 984 case 11: 985 IncFloat(&dyPark,digitalRead(ENCODER_PINB),1,-80,80,false); 986 break; 987 case 12: 988 IncFloat(&dxPark,digitalRead(ENCODER_PINB),1,-80,80,false); 989 break; 990 case 13: 991 IncInt(&iTrackMode,digitalRead(ENCODER_PINB),1,-1,4,true); 992 break; 993 case 14: 994 IncFloat(&xzOffset,digitalRead(ENCODER_PINB),0.5,-20,20,false); 995 break; 996 case 15: 997 IncFloat(&yzOffset,digitalRead(ENCODER_PINB),0.5,-20,20,false); 998 break; 999 case 16: 1000 IncFloat(&xzH,digitalRead(ENCODER_PINB),0.25,-20,20,false); 1001 break; 1002 case 17: 1003 IncFloat(&yzH,digitalRead(ENCODER_PINB),0.25,-20,20,false); 1004 break; 1005 case 18: 1006 if (( iDoSave == 0 ) && ( iSave != 0 )) { 1007 iDoSave = 2 ; 1008 } 1009 break; 1010 case 19: 1011 IncFloat(&xMinVal,digitalRead(ENCODER_PINB),1.0,-10,50,false); 1012 break; 1013 case 20: 1014 IncFloat(&xMaxVal,digitalRead(ENCODER_PINB),1.0,-10,50,false); 1015 break; 1016 case 21: 1017 IncFloat(&yMinVal,digitalRead(ENCODER_PINB),1.0,-70,50,false); 1018 break; 1019 case 22: 1020 IncFloat(&yMaxVal,digitalRead(ENCODER_PINB),1.0,-70,50,false); 1021 break; 1022 } 1023 } 1024 prev_millis = millis(); 1025 } else { 1026 if ( prev_millis > ( millis() + 1000 ) ) { 1027 prev_millis = millis(); 1028 } 1029 } 1030 1031} 1032 1033float sign(float target) { 1034 if (target > 0 ) { 1035 return (1); 1036 } else { 1037 if (target < 0 ) { 1038 return (-1); 1039 } else { 1040 return (0); 1041 } 1042 } 1043} 1044 1045void DisplayMeatBall() { 1046 int pos , led , x , y; 1047 float dx , dy ; 1048 float dxa , dya ; 1049 1050 if (iDayNight == 1) { 1051 HT.setBrightness(15); 1052 } else { 1053 HT.setBrightness(0); 1054 } 1055 1056 dx = xzAng - xzTarget ; 1057 dy = yzAng - yzTarget ; 1058 dxa = abs(dx) ; 1059 dya = abs(dy) ; 1060 if (dxa < 6) { 1061 x = 0 ; 1062 } else { 1063 if (dxa < 12) { 1064 x = sign(dx); 1065 } else { 1066 if (dxa < 50) { 1067 x = 2 * sign(dx); 1068 } else { 1069 x = 3 * sign(dx); 1070 } 1071 } 1072 } 1073 if (dya < 6) { 1074 y = 0 ; 1075 } else { 1076 if (dya < 12) { 1077 y = sign(dy); 1078 } else { 1079 if (dya < 25) { 1080 y = 2 * sign(dy); 1081 } else { 1082 y = 3 * sign(dy); 1083 } 1084 } 1085 } 1086 pos = 27 ; // netral position 1087 pos += (y * 8) ; // add or sumtract the x in range of -3 to +3 1088 pos += (x ) ; // add or sumtract 8 * y or y in range of -3 to +3 1089 for (led = 0; led < 63; led++) { 1090 switch (led){ 1091 case 0: 1092 case 7: 1093 case 56: 1094 case 63: 1095 break; 1096 default: 1097 HT.clearLedNow(MapLedNo(led)); 1098 break; 1099 } 1100 } 1101 1102 HT.setLedNow(MapLedNo(0)); // turn on four courners 1103 HT.setLedNow(MapLedNo(7)); 1104 HT.setLedNow(MapLedNo(56)); 1105 HT.setLedNow(MapLedNo(63)); 1106 1107 1108 // HT.setLedNow(MapLedNo(tc.sec)); 1109 if ((digitalRead(4) == HIGH) && (digitalRead(5) == HIGH) && (digitalRead(6) == HIGH) && (digitalRead(7) == HIGH)) { 1110 HT.setBlinkRate(HT16K33_DSP_NOBLINK); // not attempting to move 1111 if (tc.sec % 2 == 0 ) { 1112 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1113 HT.setLedNow(MapLedNo(pos + 9)); 1114 }else{ 1115 HT.setLedNow(MapLedNo(pos + 1)); 1116 HT.setLedNow(MapLedNo(pos + 8)); 1117 } 1118 } else { 1119 HT.setBlinkRate(HT16K33_DSP_BLINK2HZ); //moving so blink meat ball 1120 HT.setLedNow(MapLedNo(pos + 0)); // display the meatball 1121 HT.setLedNow(MapLedNo(pos + 1)); 1122 HT.setLedNow(MapLedNo(pos + 8)); 1123 HT.setLedNow(MapLedNo(pos + 9)); 1124 } 1125} 1126 1127 1128int MapLedNo(int target) // this compensates for the screwy setup of the matrix driver to the chip 1129{ 1130 int row ; 1131 int col ; 1132 row = target / 8 ; 1133 col = target % 8 ; 1134 if (col == 0 ) { 1135 return ((row * 16 ) + 7) ; 1136 } else { 1137 return ((row * 16 ) + col - 1) ; 1138 } 1139} 1140 1141
ht16k33 lib for matrix display
c_cpp
Been asked a few times for these library files
1inary file (no preview
Solar_Calcs.bas
vbscript
Early proof of concept code....
1Attribute VB_Name = "SolarStuff" 2Option Explicit 3 4Global gdblLat As Double 5Global gdblLong As Double 6 7Global Const PI = 3.14159265358979 8 9Function arccos(x) 10 arccos = Atn(-x / Sqr(-x * x + 1)) + 2 * Atn(1) 11End Function 12 13Function arcsin(x) 14 arcsin = Atn(x / Sqr(-x * x + 1)) 15End Function 16 17' 18' Fraction of a year 19' 20Function gama(Optional MyDate) As Double 21Dim dtNow As Date 22Dim dt1st As Date 23 If IsMissing(MyDate) Then 24 MyDate = Now() 25 Else 26 MyDate = CDate(MyDate) 27 End If 28 dt1st = CDate("1/1/" & Year(MyDate)) 29 gama = (2 * PI / 365) * (DateDiff("y", dt1st, MyDate) + ((Hour(MyDate) - 12) / 24)) 30' gama = (2 * PI / 364) * ((DateDiff("y", dt1st, MyDate) + ((hour(MyDate) - 12) * 60 + Minute(MyDate)) / 1440)) 31' gama = (2 * PI / 365) * (DateDiff("y", dt1st, MyDate) + ((hour(MyDate)) / 24)) 32End Function 33' 34' 35' Equation of time 36' In Minutes 37Function eqTime(g As Double) As Double 38 eqTime = 229.18 * (0.000075 + (0.001868 * Cos(g)) - (0.032077 * Sin(g)) - (0.014615 * Cos(2 * g)) - (0.040849 * Sin(2 * g))) 39 40' eqTime = (229.18 * (0.000075 + 0.001568 * Cos(g) - 0.032077 * Sin(g) - 0.014615 * Cos(2 * g) - 0.040849 * Sin(2 * g))) 41 42End Function 43' 44' 45' Declination of the sun 46' In Radians 47Function Decl(g As Double) As Double 48 Decl = 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)) 49' Decl = (0.006918 - 0.399912 * Cos(g) + 0.070257 * Sin(g) - 0.006758 * Cos(2 * g) + 0.000907 * Sin(2 * g)) 50End Function 51' 52' 53' Eq time in minutes 54' 55Function time_offset(Longitude As Double, MyTime As Date, TimeZone As Long) As Double 56 time_offset = (-4 * Longitude) + (60 * TimeZone) - eqTime(gama(MyTime)) 57End Function 58 59' 60' tst 61' In minutes 62' 63Function TrueSolarTime(Longitude As Double, MyTime As Date, TimeZone As Long) As Double 64 TrueSolarTime = (Hour(MyTime) * 60) + Minute(MyTime) + (Second(MyTime) / 60) - time_offset(Longitude, MyTime, TimeZone) 65End Function 66' 67' 68' in degrees 69' -180 to 180 70Function hourAngle(Longitude As Double, MyTime As Date, TimeZone As Long) As Double 71 hourAngle = (TrueSolarTime(Longitude, MyTime, TimeZone) / 4) - 180 ' 720 minutes is solar noon -- div 4 gives 180 72End Function 73 74 75' Hour Angle at sunset or sunrise only !!!! 76' in degrees (ie solar zenith is 90.833 deg) 77Function ha(lat As Double, MyDate As Date) As Double 78 Dim latRad As Double 79 latRad = lat * 2 * PI / 360 80 ha = arccos((Cos(90.833 * PI / 180) / (Cos(latRad) * Cos(Decl(gama(MyDate)))) - (Tan(latRad) * Tan(Decl(gama(MyDate)))))) / PI * 180 81End Function 82 83 84Function Sunrise(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 85 Sunrise = 720 - (4 * (Longitude + ha(lat, MyTime))) + (60 * TimeZone) - eqTime(gama(MyTime)) 86End Function 87Function SunriseTime(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Date 88Dim dblTmp As Double 89 dblTmp = Sunrise(Longitude, lat, MyTime, TimeZone) 90 SunriseTime = DateAdd("n", CInt(dblTmp), DateSerial(Year(MyTime), Month(MyTime), Day(MyTime))) 91End Function 92 93Function Sunset(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 94 Sunset = 720 - (4 * (Longitude - ha(lat, MyTime))) + (60 * TimeZone) - eqTime(gama(MyTime)) 95End Function 96Function SunsetTime(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Date 97Dim dblTmp As Double 98 dblTmp = Sunset(Longitude, lat, MyTime, TimeZone) 99 SunsetTime = DateAdd("n", CInt(dblTmp), DateSerial(Year(MyTime), Month(MyTime), Day(MyTime))) 100End Function 101 102Function Snoon(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 103 Snoon = 720 - (4 * Longitude) + (60 * TimeZone) - eqTime(gama(MyTime)) 104End Function 105Function SnoonTime(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Date 106Dim dblTmp As Double 107 dblTmp = Snoon(Longitude, lat, MyTime, TimeZone) 108 SnoonTime = DateAdd("n", CInt(dblTmp), DateSerial(Year(MyTime), Month(MyTime), Day(MyTime))) 109End Function 110 111Function SolarZenithRad(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 112Dim dTmp As Double 113Dim latRad As Double 114Dim decrad As Double 115Dim HourAngleRad As Double 116 117 latRad = lat * 2 * PI / 360 118 decrad = Decl(gama(MyTime)) 119 HourAngleRad = hourAngle(Longitude, MyTime, TimeZone) * PI / 180 120 121 dTmp = arccos((Sin(latRad) * Sin(decrad)) + (Cos(latRad) * Cos(decrad) * Cos(HourAngleRad))) 122 SolarZenithRad = dTmp 123End Function 124 125Function SolarElevationRad(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 126Dim dTmp As Double 127Dim latRad As Double 128Dim decrad As Double 129Dim HourAngleRad As Double 130 131 latRad = lat * 2 * PI / 360 132 decrad = Decl(gama(MyTime)) 133 HourAngleRad = hourAngle(Longitude, MyTime, TimeZone) * PI / 180 134 135 dTmp = arccos((Sin(latRad) * Sin(decrad)) + (Cos(latRad) * Cos(decrad) * Cos(HourAngleRad))) 136 SolarElevationRad = (PI / 2) - dTmp 137End Function 138 139 140Function SolarAzimuthRad(Longitude As Double, lat As Double, MyTime As Date, TimeZone As Long) As Double 141Dim dTmp As Double 142Dim latRad As Double 143Dim decrad As Double 144Dim solzenrad As Double 145Dim HourAngleRad As Double 146 147 latRad = lat * 2 * PI / 360 148 decrad = Decl(gama(MyTime)) 149 solzenrad = SolarZenithRad(Longitude, lat, MyTime, TimeZone) 150 HourAngleRad = hourAngle(Longitude, MyTime, TimeZone) * PI / 180 151 152' dTmp = arccos(-1 * (Cos(solzenrad) * Sin(latrad) - Sin(decrad)) / (Sin(solzenrad) * Cos(latrad))) 153' dTmp = arccos((Sin(decrad) - (Sin(decrad) * Sin(latrad))) / (Sin(solzenrad) * Cos(latrad))) 154 dTmp = arccos(((Sin(decrad) * Cos(latRad)) - (Cos(HourAngleRad) * Cos(decrad) * Sin(latRad))) / Sin(solzenrad)) 155 If HourAngleRad < 0 Then 156 SolarAzimuthRad = dTmp 157 Else 158 SolarAzimuthRad = (2 * PI) - dTmp 159 End If 160 161End Function 162 163' 164' ???????? nor working ????? from winkipedia 165' 166Function SolarDeclinationRad(MyDate As Date) As Double 167Dim dTmp As Double 168Dim N As Double 169 170 N = DateDiff("y", CDate("1/1/" & CStr(Year(MyDate))), MyDate) + (((Hour(MyDate) - 12) * 60 + Minute(MyDate)) / 1440) ' days since 1/1 in year 171 dTmp = -arcsin(0.39779 * Cos(0.0172029 * (N + 10) + 0.0334 * Sin(0.0172029 * (N - 2)))) ' constants converted to radians (was degrees in winkipedia) 172 SolarDeclinationRad = dTmp ' * 180 / PI ' converter to degrees 173End Function 174 175 176Function JulianDay(MyDate As Date) As Double 177 178End Function 179Function JulianCentury(MyDate As Date) As Double 180 181End Function 182 183
Downloadable files
Mud map of circuits
Suggest you follow the TI PDF on the buffer chips Need to look at photos as well , but its not rocket science ... very basic stuff
Mud map of circuits
Mud map of circuits
Suggest you follow the TI PDF on the buffer chips Need to look at photos as well , but its not rocket science ... very basic stuff
Mud map of circuits
Documentation
Frame and Piviots
This shows the gimble piece and its relation to the frame and ost
Frame and Piviots
Panels Location
This shows the relationship of the panels to the frame and the relative dimesions
Panels Location
Post top section
This is the business end of the machine. This is then aligned north and welded to the support tower The frame then locks into this via two bearings.
Post top section
Tracker Frame
This is the support frame so the panels do not flex to much in the wind
Tracker Frame
Frame and Piviots
This shows the gimble piece and its relation to the frame and ost
Frame and Piviots
Panels Location
This shows the relationship of the panels to the frame and the relative dimesions
Panels Location
Post top section
This is the business end of the machine. This is then aligned north and welded to the support tower The frame then locks into this via two bearings.
Post top section
Tracker Frame
This is the support frame so the panels do not flex to much in the wind
Tracker Frame
Comments
Only logged in users can leave comments
dougal
0 Followers
•0 Projects
+3
Work attribution
Table of contents
Intro
45
0