diff --git a/.gitignore b/.gitignore index cc91e4c..fee99c0 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,8 @@ Visual Micro/ *.orig .directory +*.o +*.d + /.project +/Logs \ No newline at end of file diff --git a/Arduheat.ino b/Arduheat.ino index edec613..1ffac79 100644 --- a/Arduheat.ino +++ b/Arduheat.ino @@ -1,12 +1,46 @@ #include -#include +// #include #include "cWarmWater.h"// important for global TimeNow variable #include "cHeating.h" #include + +/* #include "config.h" #include #include +*/ #include +#include + +/* +#include +#include +#include + +#include "fileServer.h" +*/ +// Speicherbedarf: +// 2168 ohne logging und webserver +// 4352 mit logging und webserver + +/* CHANGE THIS TO YOUR OWN UNIQUE VALUE. The MAC number should be + * different from any other devices on your network or you'll have + * problems receiving packets. */ +//static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + + +/* CHANGE THIS TO MATCH YOUR HOST NETWORK. Most home networks are in + * the 192.168.0.XXX or 192.168.1.XXX subrange. Pick an address + * that's not in use and isn't going to be automatically allocated by + * DHCP from your router. */ +//static uint8_t ip[] = { 192, 168, 123, 250 }; + + +/* This creates an instance of the webserver. By specifying a prefix + * of "", all pages will be at the root of the server. */ +//#define PREFIX "" +//WebServer webserver(PREFIX, 80); + #include "cTrigger.h" @@ -22,24 +56,24 @@ RTC_DS1307 rtc; DateTime TimeNow; //////////////////////// -cTrigger triggerLog(10000); +cTrigger trigger(10000); cHeating Heating; + /** * @fn void setup(void) * @brief Initializes the serial connection and defines the in- and outputs. */ void setup() { - pinMode(10, OUTPUT); // set the SS pin as an output (necessary!) - digitalWrite(10, HIGH); // but turn off the W5100 chip! - pinMode(53, OUTPUT); + pinMode(53, OUTPUT); // set the SS pin as an output (necessary!) + digitalWrite(53, HIGH); // but turn off the W5100 chip! //// RTC test ////////// Serial.begin(115200); - PgmPrint("Free RAM: "); // part of sdFatUtil - Serial.println(FreeRam()); + //PgmPrint("Free RAM: "); // part of sdFatUtil + //Serial.println(FreeRam()); // RTC Wire.begin(); @@ -52,16 +86,34 @@ void setup() analogReference(DEFAULT); //analogReference(EXTERNAL); + /* initSD(); - writeConf( &Heating ); // write defaults - if (!readConfig(&Heating)) { - writeConf(&Heating); // If reading config fails, write defaults - Serial.println(F("sd config file did not exist, writing defaults.")); - } - ini.close(); - writeConf( &Heating); // write defaults + writeConf(&Heating); // write defaults + readConf( &Heating); + writeConf(&Heating); // write defaults startLogging(&Heating); + + /* initialize the Ethernet adapter */ + //Ethernet.begin(mac, ip); + + //initFileServer(); + + /* setup our default command that will be run when the user accesses + * the root page on the server */ + //webserver.setDefaultCommand(&helloCmd); + + /* run the same command if you try to load /index.html, a common + * default page name */ + //webserver.addCommand("index.html", &helloCmd); + + //webserver.addCommand("rest.html", &restCmd); + + // Fileserver + //webserver.setUrlPathCommand(&fsAccessCmd); + + /* start the webserver */ + //webserver.begin(); } /** @@ -70,19 +122,32 @@ void setup() */ void loop() { - TimeNow = rtc.now(); Heating.Control(); Heating.WarmWater.Control(); - if ((triggerLog.get())&&logging) { + /* + if (trigger.get()) { + // Generate new logfile every day at midnight + if (TimeNow.hour()-rtc.now().hour()==23){ + stopLogging(); + TimeNow = rtc.now(); + startLogging(&Heating); + } + TimeNow = rtc.now(); + // if(fileerror||sderror) { // initSD(); // startLogging(&Heating); // } - logWrite(false, &Heating); + //PgmPrint("Free RAM: "); Serial.println(FreeRam());// part of sdFatUtil + if(logging) logWrite(false, &Heating); } - if (Serial.available()&&(logging)) { - stopLogging(); - } + if (Serial.available()&&(logging)) stopLogging(); + + char buff[64]; + int len = 64; + */ + /* process incoming connections one at a time forever */ + //webserver.processConnection(buff, &len); } \ No newline at end of file diff --git a/PinDefinitions.h b/PinDefinitions.h index efda2ff..3cf1696 100644 --- a/PinDefinitions.h +++ b/PinDefinitions.h @@ -21,52 +21,69 @@ #define PinWarmWaterSwitch 19 #define PinHeatControl 3 -// System Temperatures on Multiplexer 3 -#define SystemMultiplexer 2 -#define MultiplexTempHeatingReturn 0 -#define MultiplexTempHeatingLead 1 -#define MultiplexTempHeatSource1Lead 3 //2 -#define MultiplexTempHeatSource1Return 2 //3 -#define MultiplexTempHeatSource1Operation 4 -#define MultiplexTempSolarReturn 5 -#define MultiplexTempSolarLead 6 -#define MultiplexTempBoilerCharge 7 -#define MultiplexTempBoilerReserve2 8 -#define MultiplexTempBoilerReserve1 9 -#define MultiplexTempBoilerHead 10 -#define MultiplexTempBoilerTop 11 -#define MultiplexTempWarmWater 12 -#define MultiplexTempCirculationReturn 13 -#define MultiplexSolarIntensity 14 -#define MultiplexTempOutside 15 -// System Temperatures -#define OffsetTempHeatingLead 0 //6 -#define OffsetTempHeatingReturn 0 //-1 -#define OffsetTempHeatSource1Lead 0 -#define OffsetTempHeatSource1Return 0 -#define OffsetTempHeatSource1Operation 0 -#define OffsetTempSolarReturn 0 -#define OffsetTempSolarLead 0 -#define OffsetSolarIntensity 0 -#define OffsetTempBoilerCharge 0 //7 -#define OffsetTempBoilerReserve1 0 -#define OffsetTempBoilerReserve2 0 -#define OffsetTempBoilerHead 0 -#define OffsetTempBoilerTop 0 //1 -#define OffsetTempWarmWater 0 //2.35 -#define OffsetTempCirculationReturn 0 -#define OffsetTempOutside 0 -#define OffsetTempHeatControl 0 +// System Temperatures +#define idxTempHeatingReturn 0 +#define idxTempHeatingLead 1 +#define idxTempSolarFromCollector 3 //2 +#define idxTempSolarFromSystem 2 //3 +#define idxTempHeatSource1Operation 4 +#define idxTempSolarToCollector 5 +#define idxTempSolarToSystem 6 +#define idxTempBoilerCharge 7 +#define idxTempBoilerReserve2 8 +#define idxTempBoilerReserve1 9 +#define idxTempBoilerHead 10 +#define idxTempBoilerTop 11 +#define idxTempWarmWater 12 +#define idxTempWarmWaterToBoiler 13 // Not Used +#define idxSolarIntensity 14 +#define idxTempOutside 15 -// Multiplexer Control Pins -const int MultiplexControl[] = {48, 47, 46, 49}; -// Multiplexer Temperature Input Pins A12 = 66; A13 = 67; A14 = 68 -const int MultiplexInput[] = {66, 67, 68}; +#define P(str) (strcpy_P(p_buffer, PSTR(str)), p_buffer) + -const int MultiplexChannelRoomsIs[] = {14,12,10,8,6,4,2,0,14,12,10,8,6,4,2,0}; -const int MultiplexChannelRoomsSp[] = {15,13,11,9,7,5,3,1,15,13,11,9,7,5,3,1}; -const int MultiplexNumberRooms[] = { 0, 0, 0,0,0,0,0,0, 1, 1, 1,1,1,1,1,1}; +const PROGMEM int MPChanSys[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; +const PROGMEM float SysTempOffset[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-3.08}; +// On system Multiplexer 2 +const PROGMEM int MPNumSys[] = {2}; +// Multiplexer Control Pins +const PROGMEM int MPControl[] = {48, 47, 46, 49}; +// Multiplexer Temperature Input Pins A12 = 66; A13 = 67; A14 = 68 +const PROGMEM int MPInput[] = {66, 67, 68}; #endif +/* +Encoding scheme for variable names + +Objects + +Boiler B +WarmWater W +Burner Bu +Solar S +Source SO +Sink SI +Rooms R +Heating H +Mixer M +Pump P +Valve V +Tempsensor/Temperature T + +time t + +Postfix +is i +setpoint s +margin m + +need n +charge c +sufficientHeat suht + +to to +from from + +*/ \ No newline at end of file diff --git a/cBoiler.cpp b/cBoiler.cpp index 2b910d3..2d8a348 100644 --- a/cBoiler.cpp +++ b/cBoiler.cpp @@ -1,9 +1,27 @@ #include "cBoiler.h" +cBoiler::cBoiler(cRooms* Rooms_,cWarmWater* WarmWater_) +:Valve(PinValveBoilerOpen,PinValveBoilerClose), +pid( 1.05, 0.0, 10.5, REVERSE), //I= 0.063 +TempCharge((&MPNumSys[0]),(&MPChanSys[idxTempBoilerCharge]),(&SysTempOffset[idxTempBoilerCharge])), +TempReserve1((&MPNumSys[0]),(&MPChanSys[idxTempBoilerReserve1]),(&SysTempOffset[idxTempBoilerReserve1])), +TempReserve2((&MPNumSys[0]),(&MPChanSys[idxTempBoilerReserve2]),(&SysTempOffset[idxTempBoilerReserve2])), +TempHead((&MPNumSys[0]),&MPChanSys[idxTempBoilerHead],(&SysTempOffset[idxTempBoilerHead])), +TempTop((&MPNumSys[0]),&MPChanSys[idxTempBoilerTop],(&SysTempOffset[idxTempBoilerTop])), +Pump(PinPumpBoiler) +{ + Rooms = Rooms_; + WarmWater = WarmWater_; + ChargeMargin = DefaultChargeMargin; + // Set minimal Pump Power to 10% + pid.SetOutputLimits(0.1, 1); +} void cBoiler::getSP( JsonObject& root ) { - root["BoilerChargeMargin"] = ChargeMargin; + //char p_buffer[80]; + //root[P("Bcm")] = ChargeMargin; + root["Bcm"] = ChargeMargin; } int cBoiler::setSP( JsonObject& root ) @@ -11,9 +29,9 @@ int cBoiler::setSP( JsonObject& root ) int fail = 0; int posReturn =0; - if(root.containsKey("BoilerChargeMargin")) { - if(root["BoilerChargeMargin"].is()) { - ChargeMargin = root["BoilerChargeMargin"].as(); + if(root.containsKey("Bcm")) { + if(root["Bcm"].is()) { + ChargeMargin = root["Bcm"].as(); posReturn++; } else fail=1; @@ -21,26 +39,29 @@ int cBoiler::setSP( JsonObject& root ) else fail=1; if (fail == 0) // If all three parameter objects were successfully filled - return posReturn; + return posReturn; else - return 0; + return 0; } void cBoiler::getData( JsonObject& root ) { - root["BoilerValve"] = static_cast( Valve.get()); + //char buffer[30]; + //PROGMEM prog_char BoilerValve[] = "BoilerValve"; + //strcpy_P(buffer, (char*)pgm_read_word(&(sBoilerValve))); + root["BV"] = static_cast( Valve.get()); - root["BoilerTempCharge"] = TempCharge.get(); - root["BoilerTempTop"] = TempTop.get(); - root["BoilerTempHead"] = TempHead.get(); - root["BoilerTempReserve1"] = TempReserve1.get(); - root["BoilerTempReserve2"] = TempReserve2.get(); + root["BT0"] = TempCharge.get(); + root["BT1"] = TempTop.get(); + root["BT2"] = TempHead.get(); + root["BT3"] = TempReserve1.get(); + root["BT4"] = TempReserve2.get(); - root["BoilerPumpBoilerCharge"] = PumpBoilerCharge.getPower(); - root["BoilerneedChargeWarmWater"] = static_cast( bneedChargeWarmWater); - root["BoilerneedChargeHeating"] = static_cast( bneedChargeHeating); - root["BoilerbDischarging"] = static_cast( bDischarging); - root["BoilerbCharging"] = static_cast( bCharging); - root["BoilerSpTempWarmWater"] = WarmWater->SpTemp(); - root["BoilerSpTempCharge"] = getSpTempCharge(); -} + root["BP"] = pid.get(); + root["BncW"] = static_cast( bneedChargeWarmWater); + root["BncH"] = static_cast( bneedChargeHeating); + root["Bdisc"] = static_cast( bDischarging); + root["Bc"] = static_cast( bCharging); + root["BTsW"] = WarmWater->SpTemp(); + root["BTsc"] = getSpTempCharge(); +} \ No newline at end of file diff --git a/cBoiler.h b/cBoiler.h index fbb5b43..eb5804c 100644 --- a/cBoiler.h +++ b/cBoiler.h @@ -11,29 +11,18 @@ #include #include +#include + + // Setpoint System Temperatures -#define DefaultChargeMargin 4.0 +#define DefaultChargeMargin 5.0 +#define DefaultSpMargin 3.0 class cBoiler { public: /// Creates a Boiler object - cBoiler(cRooms* Rooms_,cWarmWater* WarmWater_) - :Valve(PinValveBoilerOpen,PinValveBoilerClose), - PIDBoilerCharge( 0.2, 0.1, 0.05, REVERSE), - TempCharge(SystemMultiplexer,MultiplexTempBoilerCharge,OffsetTempBoilerCharge), - TempReserve1(SystemMultiplexer,MultiplexTempBoilerReserve1,OffsetTempBoilerReserve1), - TempReserve2(SystemMultiplexer,MultiplexTempBoilerReserve2,OffsetTempBoilerReserve2), - TempHead(SystemMultiplexer,MultiplexTempBoilerHead,OffsetTempBoilerHead), - TempTop(SystemMultiplexer,MultiplexTempBoilerTop,OffsetTempBoilerTop), - PumpBoilerCharge(PinPumpBoiler) - { - Rooms = Rooms_; - WarmWater = WarmWater_; - ChargeMargin = DefaultChargeMargin; - // Set minimal Pump Power to 10% - PIDBoilerCharge.SetOutputLimits(0.1, 1); - } + cBoiler(cRooms* Rooms_,cWarmWater* WarmWater_); double getSpTempCharge(void) { @@ -54,8 +43,8 @@ class cBoiler { // Hysteresis by top and head temperature sensors. // If top falls below setpoint: charge. If head gets above setpoint: dont charge. - if (WarmWater->SpTemp() > TempTop.get()) bneedChargeWarmWater = true; - if (WarmWater->SpTemp() < TempHead.get()) bneedChargeWarmWater = false; + if (WarmWater->SpTemp()+DefaultSpMargin > TempTop.get()) bneedChargeWarmWater = true; + if (WarmWater->SpTemp()+DefaultSpMargin < TempHead.get()) bneedChargeWarmWater = false; return bneedChargeWarmWater; } @@ -71,16 +60,16 @@ class cBoiler return bneedChargeHeating; } - void charge(boolean bCharge) + void charge(boolean bCharge, float TempChargeSource) { bCharging = bCharge; Valve.set((bDischarging || bCharging)); // Open Valve if charging or discharging if (bCharge) // Run Pump - PumpBoilerCharge.setPower(PIDBoilerCharge.run(getSpTempCharge(), TempCharge.get())); + Pump.setPower(pid.run(getSpTempCharge(), TempChargeSource)); else // Stop Charging: Stop PID and Pump - PumpBoilerCharge.setPower(PIDBoilerCharge.run()); + Pump.setPower(pid.run()); } void discharge( boolean bNeedSourceBoiler ) @@ -97,8 +86,8 @@ class cBoiler void getData(JsonObject& root); cValve Valve; - cPump PumpBoilerCharge; - cPID PIDBoilerCharge; + cPump Pump; + cPID pid; // May become private again (debug) cTempSensor TempCharge; @@ -117,6 +106,8 @@ class cBoiler boolean bneedChargeHeating; boolean bDischarging; boolean bCharging; + + }; #endif diff --git a/cBurner.cpp b/cBurner.cpp index 12d6e5d..dae27c3 100644 --- a/cBurner.cpp +++ b/cBurner.cpp @@ -2,8 +2,8 @@ void cBurner::getSP( JsonObject& root ) { - root["BurnerMaxTempOperation"] = MaxTempOperation; - root["BurnerMinBurnTime"] = MinBurnTime; + root["BuTmax"] = MaxTempOperation; + root["ButMin"] = MinBurnTime; } int cBurner::setSP( JsonObject& root ) @@ -11,18 +11,18 @@ int cBurner::setSP( JsonObject& root ) int fail = 0; int posReturn =0; - if(root.containsKey("BurnerMaxTempOperation")) { - if(root["BurnerMaxTempOperation"].is()) { - MaxTempOperation = root["BurnerMaxTempOperation"].as(); + if(root.containsKey("BuTmax")) { + if(root["BuTmax"].is()) { + MaxTempOperation = root["BuTmax"].as(); posReturn++; } else fail=1; } else fail=1; - if(root.containsKey("BurnerMinBurnTime")) { - if(root["BurnerMinBurnTime"].is()) { - MinBurnTime = static_cast(root["BurnerMinBurnTime"].as()); + if(root.containsKey("ButMin")) { + if(root["ButMin"].is()) { + MinBurnTime = static_cast(root["ButMin"].as()); posReturn++; } else fail=1; @@ -40,12 +40,10 @@ int cBurner::setSP( JsonObject& root ) void cBurner::getData( JsonObject& root ) { // Objects - root["BurnerValve"] = static_cast(Valve.get()); - root["BurnerTempLead"] = TempLead.get(); - root["BurnerTempReturn"] = TempReturn.get(); - root["BurnerTempOperation"] = TempOperation.get(); + root["BuV"] = static_cast(Valve.get()); + root["BuT"] = TempOperation.get(); // Variables - root["BurnerStartTime"] = StartTime; - root["BurnersufficientHeat"] = static_cast(sufficientHeat); - root["BurnerbFlame"] = static_cast(bFlame); + root["BuStartt"] = StartTime; + root["Busuht"] = static_cast(sufficientHeat); + root["BuBurns"] = static_cast(bFlame); } diff --git a/cBurner.h b/cBurner.h index 51c694b..20083c4 100644 --- a/cBurner.h +++ b/cBurner.h @@ -16,9 +16,7 @@ class cBurner public: cBurner(void) :Valve(PinValveHeatSource1Open,PinValveHeatSource1Close), - TempLead(SystemMultiplexer,MultiplexTempHeatSource1Lead,OffsetTempHeatSource1Lead), - TempReturn(SystemMultiplexer,MultiplexTempHeatSource1Return,OffsetTempHeatSource1Return), - TempOperation(SystemMultiplexer,MultiplexTempHeatSource1Operation,OffsetTempHeatSource1Operation) + TempOperation((&MPNumSys[0]),(&MPChanSys[idxTempHeatSource1Operation]),(&SysTempOffset[idxTempHeatSource1Operation])) { pinMode(PinStartHeatSource1, OUTPUT); @@ -31,7 +29,7 @@ class cBurner MinBurnTime = static_cast(DefaultMinBurnTimeMinutes)* static_cast(60000); } - boolean burn(boolean bShallBurn, double SpTempSource) + boolean burn(double SpTempSource, boolean bShallBurn = false) { // Start Burner flame and residual heat sequence @@ -64,9 +62,7 @@ class cBurner boolean isBurning(void) {return bFlame;}; - cTempSensor TempLead; cTempSensor TempOperation; - cTempSensor TempReturn; void getSP(JsonObject& root); int setSP(JsonObject& root); @@ -81,7 +77,7 @@ class cBurner unsigned long StartTime; unsigned long MinBurnTime; - double MaxTempOperation; + float MaxTempOperation; }; #endif diff --git a/cHeating.cpp b/cHeating.cpp index 44c3ead..a0bab2f 100644 --- a/cHeating.cpp +++ b/cHeating.cpp @@ -13,6 +13,7 @@ Solar() // Initialize Control line for Remote Heating system pinMode(PinHeatControl, OUTPUT); SpTempSource=0.0; + TempSource=0.0; } void cHeating::Control(void){ @@ -27,9 +28,6 @@ void cHeating::Control(void){ selectSink(SiOff); selectSource(SoOff); } - - // Run solar pump to avoid boiling - Solar.probe(true); } @@ -44,19 +42,11 @@ void cHeating::checkSinks(void) } // #2 Determine whether there is need for heating the rooms else if (Rooms.need()) { - // Trigger hysteresis to keep burner burning - Boiler.needChargeHeating(Rooms.need()); Sink = SiChargeRooms; SpTempSource = Rooms.getSpHeating(); needSource = true; } - //// #3 Determine need to charge heating - //else if (Boiler.needChargeHeating(Rooms.need())) { - //// Boiler Heating needs charging and rooms need heat - //Sink = SiChargeHeating; - //SpTempSource = Boiler.getSpTempCharge(); - //needSource = true; - //} + // Else charge into boiler else { Sink = SiChargeHeating; SpTempSource = Boiler.getSpTempCharge(); @@ -71,26 +61,31 @@ void cHeating::checkSources(void) // #1 If burner is burning: execute burner if(Burner.isBurning()) { Source = SoBurner; + TempSource = Burner.TempOperation.get(); needSink = true; } // #2 If burner is not burning: Burner residual heat: true if temperature high enough - else if (Burner.burn(false,SpTempSource)) { + else if (Burner.burn(SpTempSource, false)) { Source = SoBurnerResHeat; + TempSource = Burner.TempOperation.get(); needSink = true; } // #3 Solar - else if (false) { + else if (Solar.harvest(SpTempSource, true)) { Source = SoSolar; + TempSource = Solar.TempToSystem.get(); needSink = true; } // #4 Boiler else if (! (Boiler.needChargeWarmWater() || Boiler.needChargeHeating(Rooms.need())) ) { Source = SoBoiler; + TempSource = Boiler.TempCharge.get(); needSink = false; } // #5 Start Burner else if (needSource) { Source = SoBurner; + TempSource = Burner.TempOperation.get(); needSink = true; Boiler.triggerChargeWarmWater(); } @@ -103,40 +98,44 @@ void cHeating::selectSource( int Source ) case SoBurner: { // Check hysteresis for charging the boiler if( Boiler.needChargeWarmWater() || Boiler.needChargeHeating(Rooms.need()) ) - Burner.burn(true, SpTempSource); + Burner.burn(SpTempSource, true); else - Burner.burn(false, SpTempSource); + Burner.burn(SpTempSource, false); // Deactivate other heat sources + Solar.harvest(SpTempSource, false); Boiler.discharge(false); break; } case SoBurnerResHeat: { // Residual heat mode // Deactivate other heat sources - Burner.burn(false, SpTempSource); + Burner.burn(SpTempSource, false); + Solar.harvest(SpTempSource, true); Boiler.discharge(false); break; } case SoSolar: { // Solar mode + Solar.harvest(SpTempSource, true); // Deactivate other heat sources - Burner.burn(false, SpTempSource); + Burner.burn(SpTempSource, false); Boiler.discharge(false); break; } case SoBoiler: { // Boiler source mode Boiler.discharge(true); + Solar.harvest(SpTempSource, true); // Deactivate other heat sources - Burner.burn(false, SpTempSource); + Burner.burn(SpTempSource, false); break; } case SoOff: default: { + Solar.harvest(SpTempSource, true); // Deactivate all heat sources - // Solar Boiler.discharge(false); - Burner.burn(false, SpTempSource); + Burner.burn(SpTempSource, false); break; } } @@ -146,25 +145,25 @@ void cHeating::selectSink( int Sink ) { switch (Sink) { case SiChargeWarmWater: { - Boiler.charge(true); Rooms.ChargeRooms(false); + Boiler.charge(true, TempSource); break; } case SiChargeRooms: { - // TODO: Solar überschuss abholen - boolean ChargeBoilerAndRooms = (Boiler.getSpTempCharge() < Burner.TempOperation.get()); + boolean ChargeBoilerAndRooms = (Burner.TempOperation.get() > Boiler.getSpTempCharge()) // Burner residual heat + ||(Solar.TempFromCollector.get()-4 > Boiler.getSpTempCharge()); // Solar residual heat Rooms.ChargeRooms(true, ChargeBoilerAndRooms); - Boiler.charge(ChargeBoilerAndRooms); + Boiler.charge(ChargeBoilerAndRooms, TempSource); break; } case SiChargeHeating: { - Rooms.ChargeRooms(true); - Boiler.charge(true); + Rooms.ChargeRooms(false); + Boiler.charge(true, TempSource); break; } case SiOff: default: { Rooms.ChargeRooms(false); - Boiler.charge(false); + Boiler.charge(false, TempSource); break; } } @@ -172,11 +171,11 @@ void cHeating::selectSink( int Sink ) void cHeating::getData( JsonObject& root ) { - root["SpTempSource"] = SpTempSource; + root["SOTs"] = SpTempSource; - root["needSource"] = static_cast(needSource); - root["needSink"] = static_cast(needSink); + root["nSO"] = static_cast(needSource); + root["nSI"] = static_cast(needSink); - root["Source"] = static_cast(Source); - root["Sink"] = static_cast(Sink); + root["SO"] = static_cast(Source); + root["SI"] = static_cast(Sink); } diff --git a/cHeating.h b/cHeating.h index 205c8ac..863a2f8 100644 --- a/cHeating.h +++ b/cHeating.h @@ -9,6 +9,7 @@ #include "cBurner.h" #include "cWarmWater.h" #include "cSolar.h" + #include class cHeating @@ -21,7 +22,6 @@ class cHeating cBurner Burner; cSolar Solar; - cHeating(void); void Control(void); @@ -34,13 +34,14 @@ class cHeating private: - double SpTempSource; + float SpTempSource; + float TempSource; boolean needSource; boolean needSink; - enum Sinks {SiChargeWarmWater=1, SiChargeHeating=2, SiChargeRooms=3, SiOff=5} Sink; - enum Sources {SoBurner=1, SoBurnerResHeat=2, SoSolar=3, SoBoiler=4, SoOff=5} Source; + enum Sinks {SiChargeWarmWater, SiChargeHeating, SiChargeRooms, SiOff} Sink; + enum Sources {SoBurner, SoBurnerResHeat, SoSolar, SoBoiler, SoOff} Source; }; diff --git a/cLFPWM.h b/cLFPWM.h index 9c7ec48..775a10c 100644 --- a/cLFPWM.h +++ b/cLFPWM.h @@ -13,11 +13,13 @@ class cLFPWM { SampleTime = SampleTime_; LastTime = millis(); - Power = 0.0; } - boolean get( void ) + boolean get( double Power ) { + // Limit Power + Power = min(max(Power,0),1); + unsigned long elapsedTime = millis()-LastTime; // Interval in which the PWM is high: @@ -37,18 +39,9 @@ class cLFPWM return active; } - boolean get( double Power_ ) - { - // Adjust power level - Power = min(max(Power_,0),1); - // get state of PWM cycle - return get(); - } - private: unsigned long SampleTime; unsigned long LastTime; - double Power; }; #endif diff --git a/cMixer.h b/cMixer.h index 9cb3aea..7c253ce 100644 --- a/cMixer.h +++ b/cMixer.h @@ -7,12 +7,7 @@ class cMixer { - private: - cLFPWM PWM; - int pinOpen; - int pinClose; - - public: + public: /// Creates the mixer cMixer(int pinOpen_, int pinClose_):PWM(5000){ pinOpen = pinOpen_; @@ -30,7 +25,7 @@ class cMixer boolean direction = (Power>=0); // Check PWM and hysteresis - if(PWM.get(abs(Power))&&(abs(Power)> 0.1)) { + if(PWM.get(abs(Power))&&(abs(Power)> 0.0)) { // Drive in direction digitalWrite(pinClose, direction); digitalWrite(pinOpen, !direction); @@ -41,6 +36,13 @@ class cMixer digitalWrite(pinOpen, HIGH); } } + + //float get(void){return PWM.getPower();} + + private: + cLFPWM PWM; + int pinOpen; + int pinClose; }; #endif diff --git a/cPump.h b/cPump.h index 87cba69..83f3bf3 100644 --- a/cPump.h +++ b/cPump.h @@ -4,8 +4,9 @@ #include "Arduino.h" #include "PinDefinitions.h" #include "cLFPWM.h" +#include -#define PWMPeriod 4000 +#define PWMPeriod 2000 class cPump { @@ -14,32 +15,31 @@ class cPump /** The power of the pump is set to zero by default.*/ /** \param PinPump the pin used to drive the Pump*/ /** \param Power the power the pump in percent*/ - cPump(int PinPump_, double Power_ = 0.0):PWM(PWMPeriod) + cPump(int PinPump_,float p= 0.0, float i= 0.0, float d= 0.0, double Power = 0.0): + PWM(PWMPeriod), + pid(p, i, d, DIRECT) { PinPump = PinPump_; pinMode(PinPump, OUTPUT); - setPower(Power_); + setPower(Power); } /// Set the power of the pump in percent /** Set the power of the pump in percent and execute \param Value of the desired power in percent*/ - void setPower( double Power_ ) + void setPower( double Power ) { // Limit Power - Power = max(min(Power_,1.0),0.0); + Power = max(min(Power,1.0),0.0); // Pump is running if switching time of PWM is not yet exceeded digitalWrite(PinPump, !PWM.get(Power)); // Pump switched on if true and off if false } - /// Get the power of the pump in percent - /** \return power of the pump in percent */ - double getPower(void){return Power;} + cPID pid; private: int PinPump; - double Power; cLFPWM PWM; diff --git a/cRoom.cpp b/cRoom.cpp index d13b9ff..8d22406 100644 --- a/cRoom.cpp +++ b/cRoom.cpp @@ -1,27 +1,52 @@ #include "cRoom.h" #include "cRooms.h" +/// Creates a room. +/** As the rooms get created in an array, only the default constructor can be used cRoom(void) */ +cRoom::cRoom(int RoomNumber , cRooms* Rooms_ ): +IsTemp(&MultiplexNumberRooms[RoomNumber], &MultiplexChannelRoomsIs[RoomNumber], &RoomIsOffset[RoomNumber]), +SpTempOverride(&MultiplexNumberRooms[RoomNumber], &MultiplexChannelRoomsSp[RoomNumber], &RoomSpOffset[RoomNumber]), +Valve(&RoomValvePin[RoomNumber]) +{ + RoomType = static_cast(pgm_read_word(&DefaultRoomType[RoomNumber])); + Rooms = Rooms_; +} + double cRoom::getSpTemp(void) { DayTypes DayType = getDayType( TimeNow.dayOfWeek() ); - TimeSpan rel; - rel.set(0, TimeNow.hour(), TimeNow.minute(), 0); + TimeSpan Now; + Now.set(0, TimeNow.hour(), TimeNow.minute(), 0); double MasterSPTemp = 0.0; // Check if manual override is valid if((SpTempOverride.get()TempRoomLow)) MasterSPTemp = SpTempOverride.get(); else - MasterSPTemp = Rooms->RoomTemps[(RoomType)]; + MasterSPTemp = Rooms->MasterSpTemps[(RoomType)]; + + double SpTemp = MasterSPTemp + Rooms->TempOffsetSchedule[Rooms->SetType][RoomType][DayType][nSwitch-1].temp; - double SpTemp = MasterSPTemp; for(int iSwitch=0; iSwitchTempOffsetSchedule[Rooms->SetType][RoomType][DayType][iSwitch].time.totalseconds() < rel.totalseconds()) + { // Apply offset according to schedule if time of schedule is later than Lastrent and sooner than + if (Rooms->TempOffsetSchedule[Rooms->SetType][RoomType][DayType][iSwitch].time.totalseconds() < Now.totalseconds()) SpTemp = MasterSPTemp + Rooms->TempOffsetSchedule[Rooms->SetType][RoomType][DayType][iSwitch].temp; } - // Check Limits + //Check Limits SpTemp = min(max(SpTemp,TempRoomLow),TempRoomHigh); return SpTemp; -} \ No newline at end of file +} + +const float cRoom::RoomIsOffset[] = {-2.5, -1.47, -1.37, 0.6, -1.5, 1.84, -1.44, -1.65, -0.4, -1.35, -0.6, 0.6, 0.6, -2.2, -1.04, -0.07}; +const float cRoom::RoomSpOffset[] = {0.77, 2.0436198179979925, 1.6490293225480279, 1.4206471183013285, -0.95, 1.0153993933264864, 2.1353791708794319, 2.4985338725986033, -0.43, 2.1375530839231054, 2.2762386248736455, 1.225520728008032, 0.36619817997989657, 1.0406370070778195, 1.5027704752275994, 1.5950556117289487}; + +// room valve pinout mapping +const int cRoom::RoomValvePin[] = {23, 24, 22, 25, 15, 26, 14, 27, 5, 28, 34, 29, 33, 30, 32, 31}; + +// Default room types mapping +const RoomTypes cRoom::DefaultRoomType[] = {Side,Side,Side,Side,Living,Living,Living,Hallway,Hallway,Hallway,Sleeping,Sleeping,Bath,Side,Side,Hallway}; + +const int cRoom::MultiplexChannelRoomsIs[] = {14,12,10,8,6,4,2,0,14,12,10,8,6,4,2,0}; +const int cRoom::MultiplexChannelRoomsSp[] = {15,13,11,9,7,5,3,1,15,13,11,9,7,5,3,1}; +const int cRoom::MultiplexNumberRooms[] = { 0, 0, 0,0,0,0,0,0, 1, 1, 1,1,1,1,1,1}; \ No newline at end of file diff --git a/cRoom.h b/cRoom.h index 4f4a540..8592733 100644 --- a/cRoom.h +++ b/cRoom.h @@ -8,24 +8,17 @@ // RTC for DateTime class #include +#include + #define TempRoomLow 10 #define TempRoomHigh 30 -#define RoomMargin 0.5 +#define RoomMargin 0.3 enum RoomTypes {Living, Sleeping, Hallway, Bath, Side, nRoomTypes}; -enum SetTypes {Normal, Holiday, nSetTypes}; +// enum SetTypes {Normal, Holiday, nSetTypes}; +enum SetTypes {Normal, nSetTypes}; enum DayTypes {Weekend, Workday, nDayTypes}; -// Calibration by measurements -const double RoomIsOffset[] = {-2.2, -1.6, -2.1, 0.6, -1.5, 0.75, -1.7, 0.09, -2.21, -1, -1, 0.55, 0.3, -2, -2.2, -0.61}; -const double RoomSpOffset[] = {2.6722851365013511, 2.0436198179979925, 1.6490293225480279, 1.4206471183013285, 1.0462285136502452, 1.0153993933264864, 2.1353791708794319, 2.4985338725986033, 5.2720930232559837, 2.1375530839231054, 2.2762386248736455, 1.225520728008032, 0.36619817997989657, 1.0406370070778195, 1.5027704752275994, 1.5950556117289487}; - -// room valve pinout mapping -const int RoomValvePin[] = {23, 24, 22, 25, 15, 26, 14, 27, 5, 28, 34, 29, 33, 30, 32, 31}; - -// Default room types mapping -const RoomTypes DefaultRoomType[] = {Side,Side,Side,Side,Living,Living,Living,Hallway,Hallway,Hallway,Sleeping,Sleeping,Bath,Side,Side,Hallway}; - extern DateTime TimeNow; // Forward declaration of cRooms class @@ -36,14 +29,7 @@ class cRoom public: /// Creates a room. /** As the rooms get created in an array, only the default constructor can be used cRoom(void) */ - cRoom(int RoomNumber , cRooms* Rooms_ ): - IsTemp(MultiplexNumberRooms[RoomNumber],MultiplexChannelRoomsIs[RoomNumber],RoomIsOffset[RoomNumber]), - SpTempOverride(MultiplexNumberRooms[RoomNumber],MultiplexChannelRoomsSp[RoomNumber],RoomSpOffset[RoomNumber]), - Valve(RoomValvePin[RoomNumber]) - { - RoomType = DefaultRoomType[RoomNumber]; - Rooms = Rooms_; - } + cRoom(int RoomNumber , cRooms* Rooms_ ); /// Compute need of room. double getNeed(void) @@ -77,6 +63,20 @@ class cRoom private: cRoomValve Valve; + // Calibration by measurements + + static const PROGMEM int MultiplexChannelRoomsIs[]; + static const PROGMEM int MultiplexChannelRoomsSp[]; + static const PROGMEM int MultiplexNumberRooms[]; + + static const PROGMEM float RoomIsOffset[]; + static const PROGMEM float RoomSpOffset[]; + + // room valve pinout mapping + static const PROGMEM int RoomValvePin[]; + + // Default room types mapping + static const PROGMEM RoomTypes DefaultRoomType[]; }; diff --git a/cRoomValve.h b/cRoomValve.h index 85fd4df..a588633 100644 --- a/cRoomValve.h +++ b/cRoomValve.h @@ -8,12 +8,12 @@ class cRoomValve { public: /// Creates a valve object without setting the pins and closed state (false). This is necessary for the rooms. - cRoomValve(int pinOpen_ = 0, int pinClose_ = 0) + cRoomValve(const int* pinOpen_ , int pinClose_ = 0) { pinOpen = pinOpen_; //pinClose=0; - pinMode(pinOpen, OUTPUT); + pinMode(pgm_read_word(pinOpen), OUTPUT); set(false); } @@ -26,12 +26,12 @@ class cRoomValve void set(boolean bState) { - digitalWrite(pinOpen, !bState); // Valve gets opened on low/false + digitalWrite(pgm_read_word(pinOpen), !bState); // Valve gets opened on low/false // digitalWrite(pinClose, !!bState); } private: - int pinOpen; + const int* pinOpen; //int pinClose; }; diff --git a/cRooms.cpp b/cRooms.cpp index 0955e5e..936bfc0 100644 --- a/cRooms.cpp +++ b/cRooms.cpp @@ -1,17 +1,17 @@ #include "cRooms.h" cRooms::cRooms( void ): -IsTempHeatingLead(SystemMultiplexer,MultiplexTempHeatingLead,OffsetTempHeatingLead), -IsTempHeatingReturn(SystemMultiplexer,MultiplexTempHeatingReturn,OffsetTempHeatingReturn), -TempOutside(SystemMultiplexer,MultiplexTempOutside,OffsetTempOutside), -PIDPump( 0.2, 0.1, 0.05, DIRECT), -PIDMixer( 0.5, 0.0, 0.005, DIRECT), -Pump(PinPumpHeating), +IsTempHeatingLead((&MPNumSys[0]),(&MPChanSys[idxTempHeatingLead]),(&SysTempOffset[idxTempHeatingLead])), +IsTempHeatingReturn((&MPNumSys[0]),(&MPChanSys[idxTempHeatingReturn]),(&SysTempOffset[idxTempHeatingReturn])), +TempOutside((&MPNumSys[0]),(&MPChanSys[idxTempOutside]),(&SysTempOffset[idxTempOutside])), +PIDPump( 0.5, 0.0, 0.0, DIRECT), +PIDMixer( 0.2, 0.0, 2.0, DIRECT), +Pump(PinPumpHeating,0.5, 0.0, 0.0, DIRECT), Mixer(PinMixerOpen,PinMixerClose) { SetType = Normal; // Initialize PID controllers for pumps - PIDPump.SetOutputLimits(0.4, 1.0); + PIDPump.SetOutputLimits(0.3, 1.0); PIDMixer.SetOutputLimits(-1.0, 1.0); PIDMixer.SetSampleTime(500); @@ -23,11 +23,12 @@ Mixer(PinMixerOpen,PinMixerClose) void cRooms::initDefaultSetpoint() { - RoomTemps[Living] = 20.0;//15;// - RoomTemps[Sleeping] =20.0;//15;// - RoomTemps[Hallway] = 19.0;//15;// - RoomTemps[Bath] = 22.0;//15;// - RoomTemps[Side] = 16.0;//15;// +// TempOffsetSchedule =20.0; + MasterSpTemps[Living] = 20.0;//15;// + MasterSpTemps[Sleeping] =20.0;//15;// + MasterSpTemps[Hallway] = 19.0;//15;// + MasterSpTemps[Bath] = 22.0;//15;// + MasterSpTemps[Side] = 15.0;//15;// double temp[nSwitch]; temp[0] = 0.0; @@ -78,7 +79,7 @@ void cRooms::ChargeRooms( boolean ChargeRooms, boolean BoilerCharges ) { dSpTempHeatingLead = getSpHeating(); dIsTempHeatingLead = IsTempHeatingLead.get(); - dSpTempHeatingReturn = dIsTempHeatingLead-DiffTempHeatingLeadReturn; + dSpTempHeatingReturn = getSpHeating()-DiffTempHeatingLeadReturn; dIsTempHeatingReturn = IsTempHeatingReturn.get(); if (ChargeRooms) @@ -114,9 +115,8 @@ void cRooms::ChargeRooms( boolean ChargeRooms, boolean BoilerCharges ) boolean cRooms::need(void) { // Reset Values - double dneedChargeRooms = 0.0; dMaxSp = 0.0; - dMaxDiff = 0.0; + dMaxDiff = -10.0; // Read state of rooms for(int i = 0; idMaxSp) dMaxSp = Room[i].getSpTemp(); // Store maximum sp-is difference - if ((Room[i].getSpTemp() - Room[i].IsTemp.get()) > dMaxDiff) - dMaxDiff = Room[i].getSpTemp() - Room[i].IsTemp.get(); +// if ((Room[i].getSpTemp() - Room[i].IsTemp.get()) > dMaxDiff) +// dMaxDiff = Room[i].getSpTemp() - Room[i].IsTemp.get(); // Check for rooms needing heat - if (Room[i].getNeed()>dneedChargeRooms) - dneedChargeRooms = Room[i].getNeed(); + if (Room[i].getNeed()>dMaxDiff) + dMaxDiff = Room[i].getNeed(); } // Hysteresis for charging the rooms - if (dneedChargeRooms > RoomMargin) needCharge = true; - if (dneedChargeRooms < -RoomMargin) needCharge = false; + if (dMaxDiff-RoomMargin > 0) needCharge = true; + if (dMaxDiff+RoomMargin < 0) needCharge = false; return needCharge; } @@ -154,7 +154,7 @@ double cRooms::getSpHeating(void) void cRooms::getOffsetTime( JsonObject& root ) { - JsonArray& times = root.createNestedArray("times"); + JsonArray& times = root.createNestedArray("Rt"); // Iterate over all sets (At home, away) for(int iSet = 0; iSet()){ - JsonArray& times = root["times"]; + if(root.containsKey("Rt")) { + if(root["Rt"].is()){ + JsonArray& times = root["Rt"]; if (times.size()==(nSetTypes*nRoomTypes*nDayTypes*nSwitch)) { @@ -205,7 +205,7 @@ int cRooms::setOffsetTime( JsonObject& root ) void cRooms::getOffsetTemp( JsonObject& root ) { - JsonArray& temps = root.createNestedArray("temps"); + JsonArray& temps = root.createNestedArray("RTs"); // Iterate over all sets (At home, away) for(int iSet = 0; iSet()){ - JsonArray& temps = root["temps"]; + if(root.containsKey("RTs")) { + if(root["RTs"].is()){ + JsonArray& temps = root["RTs"]; if (temps.size()==(nSetTypes*nRoomTypes*nDayTypes*nSwitch)) { @@ -256,15 +256,15 @@ int cRooms::setOffsetTemp( JsonObject& root ) void cRooms::getRooms( JsonObject& root ) { - JsonArray& RoomsTypes = root.createNestedArray("RoomTypes"); - JsonArray& RoomsTemps = root.createNestedArray("RoomTemps"); + JsonArray& RoomsTypes = root.createNestedArray("RTypes"); + JsonArray& RoomsTemps = root.createNestedArray("RTs"); for (int i = 0; i()){ - JsonArray& RoomsTypes = root["RoomTypes"]; + if(root.containsKey("RTypes")) { + if(root["RTypes"].is()){ + JsonArray& RoomsTypes = root["RTypes"]; if (RoomsTypes.size()==nRooms) { for (int i = 0; i()) Room[i].RoomType = static_cast(RoomsTypes[i].as()); @@ -290,12 +290,12 @@ int cRooms::setRooms( JsonObject& root ) posReturn++; } - if(root.containsKey("RoomTemps")) { - if(root["RoomTemps"].is()){ - JsonArray& RoomsTemps = root["RoomTemps"]; + if(root.containsKey("RTs")) { + if(root["RTs"].is()){ + JsonArray& RoomsTemps = root["RTs"]; if (RoomsTemps.size()==nRoomTypes) { for (int i = 0; i()) RoomTemps[i] = RoomsTemps[i].as(); + if ( RoomsTemps[i].is()) MasterSpTemps[i] = RoomsTemps[i].as(); else fail=1; } } @@ -324,19 +324,20 @@ int cRooms::setRooms( JsonObject& root ) void cRooms::getData( JsonObject& root ) { - JsonArray& RoomsIsTemps = root.createNestedArray("RoomIs"); - JsonArray& RoomsSPTemps = root.createNestedArray("RoomSP"); + JsonArray& RoomsIsTemps = root.createNestedArray("RTi"); + JsonArray& RoomsSPTemps = root.createNestedArray("RTs"); for (int i = 0; i( Valve.get()); + root["Ssuht"] = static_cast( sufficientHeat); + root["Sprobing"] = static_cast( probing); +} \ No newline at end of file diff --git a/cSolar.h b/cSolar.h index efe1663..82c8c74 100644 --- a/cSolar.h +++ b/cSolar.h @@ -6,23 +6,95 @@ #include "cTemp.h" #include "cValve.h" #include "cPump.h" +#include +#include +// 5min*60sec/min*1000ms/s = 300000ms +#define ProbeInterval 600000 + +extern DateTime TimeNow; class cSolar { - private: - boolean bFlame; - void run(void); - cTempSensor TempLead; - cTempSensor TempReturn; - cTempSensor Intensity; - cValve Valve; - cPump Pump; public: - cSolar(); + cSolar() + :Valve(PinValveSolarOpen,PinValveSolarClose), + pid( 0.1, 0.05, 0.025, DIRECT), + Pump(PinPumpSolar), + TempToCollector(&MPNumSys[0], &MPChanSys[idxTempSolarToCollector], &SysTempOffset[idxTempSolarToCollector]), + TempFromCollector(&MPNumSys[0], &MPChanSys[idxTempSolarFromCollector], &SysTempOffset[idxTempSolarFromCollector]), + TempToSystem(&MPNumSys[0], &MPChanSys[idxTempSolarToSystem], &SysTempOffset[idxTempSolarToSystem]), + TempFromSystem(&MPNumSys[0], &MPChanSys[idxTempSolarFromSystem], &SysTempOffset[idxTempSolarFromSystem]), + Intensity(&MPNumSys[0], &MPChanSys[idxSolarIntensity], &SysTempOffset[idxSolarIntensity]) + { + boolean sufficientHeat =false; + boolean probing = false; + StartTime = millis(); + + pid.SetOutputLimits(0.2, 1.0); + } + + boolean harvest(double SpTempSource, boolean enable = true) + { + // Trigger Probing + if ((millis()-StartTime>6*ProbeInterval)&&(TimeNow.hour()>9)&&(TimeNow.hour()<17)&&(!sufficientHeat)&&(!probing)) { + probing=true; + StartTime = millis(); + } + + // Harvesting hysteresis + if (TempFromCollector.get() < SpTempSource-2) // Exploit ChargeMargin of Boiler = 4 + sufficientHeat = false; + if (TempFromCollector.get() > SpTempSource+4) + sufficientHeat = true; + + if (sufficientHeat)// Run pump such that temperature difference between Return and Lead is equal to 10 degree. + { + float TempDiff = TempFromCollector.get()-SpTempSource; + Pump.setPower(pid.run(TempFromSystem.get()+TempDiff,TempToCollector.get())); + } + else if (probing) + { + Pump.setPower(pid.run(0.1)); + ; + // Check if probing interval is over + if (millis()-StartTime>ProbeInterval) { + probing = false; + StartTime = millis(); + } + } + else Pump.setPower(pid.run()); + + // Open valve if sufficient heat is available + Valve.set(sufficientHeat && enable); + + // Return if there is residual heat so that charging of boiler continues + return sufficientHeat; + } + + void getData(JsonObject& root); void probe(boolean bFlame); + + boolean sufficientHeat; + boolean probing; + + cTempSensor TempFromCollector; + cTempSensor TempToSystem; + + private: + + unsigned long StartTime; + + cTempSensor TempToCollector; + + cTempSensor TempFromSystem; + + cTempSensor Intensity; + cValve Valve; + cPump Pump; + cPID pid; }; #endif diff --git a/cTemp.h b/cTemp.h index 68db049..10ba41a 100644 --- a/cTemp.h +++ b/cTemp.h @@ -20,7 +20,7 @@ #define R25 2000.0 //! Smoothing filter coefficient #define AlphaT 10 //! Filter sampling interval -#define FilterTimePeriod 100 +#define TimePeriod 100 typedef struct{ TimeSpan time; @@ -40,63 +40,71 @@ class cTempSensor * \param dOffset * */ - cTempSensor(int MultiplexNumber_ = 0,int MultiplexChannel_ = 0, float Offset_ = 0.0): - Trigger(FilterTimePeriod) + cTempSensor(const int * MultiplexNumber_ , const int* MultiplexChannel_, const float* Offset_)//: { - pinMultiplexInput = MultiplexInput[MultiplexNumber_]; + StartTime = millis(); + + pinMultiplexInput = &MPInput[pgm_read_word(MultiplexNumber_)]; MultiplexChannel = MultiplexChannel_; + //Offset = Offset_; Offset = Offset_; - // Initialize temperature using high value to avoid switching on the burner // during initialization TempFilt = 60; - // exponential filtering coefficient [1/#Measurements] - alphaFilt = AlphaT*FilterTimePeriod/1000; - - // Multiplexer input line initializations - pinMode(MultiplexInput[0], INPUT); - pinMode(MultiplexInput[1], INPUT); - pinMode(MultiplexInput[2], INPUT); - // Multiplexer control line initializations - pinMode(MultiplexControl[0], OUTPUT); - pinMode(MultiplexControl[1], OUTPUT); - pinMode(MultiplexControl[2], OUTPUT); - pinMode(MultiplexControl[3], OUTPUT); + initMultiplexer(); } float get( void ) { - if(Trigger.get()) { + if (millis()-StartTime > TimePeriod) { + StartTime = millis(); + // Set the right multiplexer channel - digitalWrite(MultiplexControl[0],HIGH && (MultiplexChannel & B00000001)); - digitalWrite(MultiplexControl[1],HIGH && (MultiplexChannel & B00000010)); - digitalWrite(MultiplexControl[2],HIGH && (MultiplexChannel & B00000100)); - digitalWrite(MultiplexControl[3],HIGH && (MultiplexChannel & B00001000)); + digitalWrite(pgm_read_word(&MPControl[0]),HIGH && (pgm_read_word(MultiplexChannel) & B00000001)); + digitalWrite(pgm_read_word(&MPControl[1]),HIGH && (pgm_read_word(MultiplexChannel) & B00000010)); + digitalWrite(pgm_read_word(&MPControl[2]),HIGH && (pgm_read_word(MultiplexChannel) & B00000100)); + digitalWrite(pgm_read_word(&MPControl[3]),HIGH && (pgm_read_word(MultiplexChannel) & B00001000)); // Determine raw temperature reading - float Ua = analogRead(pinMultiplexInput)/1023.0*Vcc; + float Ua = analogRead(pgm_read_word(pinMultiplexInput))/1023.0*Vcc; float Ue = (Ua + Vcc*(R1/R3))/(1+R1/R2+R1/R3); float Kt = R*Ue/(Vcc-Ue) /R25; float Temp = 25+(sqrt(pow(Alpha, 2)-4*Beta+4*Beta*Kt)-Alpha)/(2*Beta); - // Apply filtering + // Apply filtering: exponential filtering coefficient [1/#Measurements] + float alphaFilt = AlphaT*TimePeriod/1000; TempFilt = (alphaFilt/(alphaFilt+1))*Temp + 1/(alphaFilt+1)*TempFilt; } // Return filtered temperature plus offset - return(TempFilt+Offset); + return(TempFilt+pgm_read_float(Offset)); } + + private: + static void initMultiplexer(void) + { + // Multiplexer input line initializations + pinMode(pgm_read_word(&MPInput[0]), INPUT); + pinMode(pgm_read_word(&MPInput[1]), INPUT); + pinMode(pgm_read_word(&MPInput[2]), INPUT); + // Multiplexer control line initializations + pinMode(pgm_read_word(&MPControl[0]), OUTPUT); + pinMode(pgm_read_word(&MPControl[1]), OUTPUT); + pinMode(pgm_read_word(&MPControl[2]), OUTPUT); + pinMode(pgm_read_word(&MPControl[3]), OUTPUT); + } + + unsigned long StartTime; + float TempFilt; - float Offset; + const float* Offset; - float alphaFilt; - int pinMultiplexInput; - int MultiplexChannel; + const int* pinMultiplexInput; + const int* MultiplexChannel; - cTrigger Trigger; }; #endif diff --git a/cWarmWater.cpp b/cWarmWater.cpp index 63a3087..8ee6498 100644 --- a/cWarmWater.cpp +++ b/cWarmWater.cpp @@ -1,11 +1,21 @@ #include "cWarmWater.h" +void cWarmWater::Control(void){ + // If Warm Water is needed, execute the pump + // with the power determined by pid controller. + // Else switch pid to manual and stop pump. + if(!digitalRead(PinWarmWaterSwitch)) + Pump.setPower(pid.run(SpTemp(), IsTemp.get())); + else + Pump.setPower(pid.run()); +} + void cWarmWater::getSP( JsonObject& root ) { - root["WWTempSchedule0time"] = TempSchedule[0].time.totalseconds(); - root["WWTempSchedule0temp"] = TempSchedule[0].temp; - root["WWTempSchedule1time"] = TempSchedule[1].time.totalseconds(); - root["WWTempSchedule1temp"] = TempSchedule[1].temp; + root["Wts0"] = TempSchedule[0].time.totalseconds(); + root["WTs0"] = TempSchedule[0].temp; + root["Wts1"] = TempSchedule[1].time.totalseconds(); + root["WTs1"] = TempSchedule[1].temp; } int cWarmWater::setSP( JsonObject& root ) @@ -13,33 +23,33 @@ int cWarmWater::setSP( JsonObject& root ) int fail = 0; int posReturn =0; - if(root.containsKey("WWTempSchedule0time")) { - if(root["WWTempSchedule0time"].is()) { - TempSchedule[0].time.set(root["WWTempSchedule0time"].as()); + if(root.containsKey("Wts0")) { + if(root["WTs0t"].is()) { + TempSchedule[0].time.set(root["WTs0t"].as()); posReturn++; } else fail=1; } else fail=1; - if(root.containsKey("WWTempSchedule0temp")) { - if(root["WWTempSchedule0temp"].is()) { - TempSchedule[0].temp = root["WWTempSchedule0temp"].as(); + if(root.containsKey("WTs0")) { + if(root["WTs0T"].is()) { + TempSchedule[0].temp = root["WTs0T"].as(); posReturn++; } else fail=1; } else fail=1; - if(root.containsKey("WWTempSchedule1time")) { - if(root["WWTempSchedule1time"].is()) { - TempSchedule[1].time.set(root["WWTempSchedule1time"].as()); + if(root.containsKey("Wts1")) { + if(root["Wts1"].is()) { + TempSchedule[1].time.set(root["Wts1"].as()); posReturn++; } else fail=1; } else fail=1; - if(root.containsKey("WWTempSchedule1temp")) { - if(root["WWTempSchedule1temp"].is()) { - TempSchedule[1].temp = root["WWTempSchedule1temp"].as(); + if(root.containsKey("WTs1")) { + if(root["WTs1"].is()) { + TempSchedule[1].temp = root["WTs1"].as(); posReturn++; } else fail=1; @@ -53,6 +63,8 @@ int cWarmWater::setSP( JsonObject& root ) void cWarmWater::getData( JsonObject& root ) { - root["WarmWaterPump"] = Pump.getPower(); - root["WarmWaterIsTemp"] = IsTemp.get(); + root["WP"] = pid.get(); + root["WTi"] = IsTemp.get(); + root["Wn"] = !digitalRead(PinWarmWaterSwitch); + root["WTitoB"] = IsTempWarmWaterToBoiler.get(); } \ No newline at end of file diff --git a/cWarmWater.h b/cWarmWater.h index b1fc2d6..0356bc9 100644 --- a/cWarmWater.h +++ b/cWarmWater.h @@ -13,8 +13,8 @@ #include #include -#define DefaultSpTempWarmWater 47.0 -#define DefaultSpTempWarmWaterLower -3.0 +#define DefaultSpTempWarmWater 45.0 +#define DefaultSpTempWarmWaterLower -2.0 extern DateTime TimeNow; @@ -24,9 +24,10 @@ class cWarmWater cWarmWater(void): Pump(PinPumpWarmWater), pid(0.5, 0.01, 0.1, DIRECT), - IsTemp(SystemMultiplexer,MultiplexTempWarmWater,OffsetTempWarmWater) + IsTemp(&MPNumSys[0], &MPChanSys[idxTempWarmWater], &SysTempOffset[idxTempWarmWater]), + IsTempWarmWaterToBoiler(&MPNumSys[0], &MPChanSys[idxTempWarmWaterToBoiler], &SysTempOffset[idxTempWarmWaterToBoiler]) { - pinMode(PinWarmWaterSwitch, OUTPUT); + pinMode(PinWarmWaterSwitch, INPUT_PULLUP); // Initialize WarmWater Schedule TempSchedule[0].time.set(0, 6,0,0); TempSchedule[0].temp = DefaultSpTempWarmWater; @@ -36,19 +37,11 @@ class cWarmWater pid.SetOutputLimits(0.0, 1.0); } - void Control(void){ - // If Warm Water is needed, execute the pump - // with the power determined by pid controller. - // Else switch pid to manual and stop pump. - if(digitalRead(PinWarmWaterSwitch)) - Pump.setPower(pid.run(SpTemp(), IsTemp.get())); - else - Pump.setPower(pid.run()); - } + void Control(void); double SpTemp(void) { - double SpTemp = 0; + double SpTemp = TempSchedule[1].temp; TimeSpan rel; rel.set(0, TimeNow.hour(), TimeNow.minute(), 0); @@ -66,9 +59,10 @@ class cWarmWater sTempSchedule TempSchedule[2]; - private: + //private: cPump Pump; cTempSensor IsTemp; + cTempSensor IsTempWarmWaterToBoiler; cPID pid; }; diff --git a/config.h b/config.h index f766bce..2630197 100644 --- a/config.h +++ b/config.h @@ -28,15 +28,9 @@ boolean fileerror = false; #define BufferSize 1000 #define LineSize 500 -#define iniFileName "System.cfg" +#define ConfigFileName "System.cfg" // Error messages stored in flash. -//#define error(msg) error_P(PSTR(msg)) -//------------------------------------------------------------------------------ -void error_P(const char* msg) { - //sd.errorHalt_P(msg); - Serial.println(msg); -} #define errorSD(msg) errorSD_F(F(msg)) @@ -57,127 +51,37 @@ void errorFile_F(const __FlashStringHelper* msg) { void initSD(void) { // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with // breadboards. use SPI_FULL_SPEED for better performance. - if (!sd.begin(chipSelect, SPI_HALF_SPEED)) { - //sd.initErrorHalt(); + if (!sd.begin(chipSelect, SPI_HALF_SPEED)) errorSD("Failed to init SDCard."); - } else { + // Reset Errors fileerror = false; sderror = false; } - } -int setObject(JsonObject& root) { - long time; - double latitude; - double longitude; - const char* sensor; - double spTemp; - - if (root.containsKey("spTemp")) { - spTemp = root["spTemp"]; - Serial.println(spTemp); - } - if (root.containsKey("time")) { - time = root["time"]; - Serial.println(time); - } - - if (root.containsKey("data")) { - latitude = root["data"][0]; - longitude = root["data"][1]; - Serial.println(latitude, 6); - Serial.println(longitude, 6); - } - - if (root.containsKey("sensor")) { - sensor = root["sensor"]; - Serial.println(sensor); - } - else { - Serial.println(F("root does not contain key sensor")); - return 0; - } - - return 1; -} - - -void getObject(JsonObject& root) { - root["sensor"] = "gps3"; - root["time"] = 1351824120; - root["spTemp"] = double(millis())/1000.5; - JsonArray& data = root.createNestedArray("data"); - data.add(48.756080, 6); // 6 is the number of decimals to print - data.add(2.302038, 6); // if not specified, 2 digits are printed -} - - -void writeConfRoomsTimes(SdFile* ini, cHeating* Heating) { +template +void writeConfelement(S* ini, T* Obj, void(T::*func)(ArduinoJson::JsonObject&)) { StaticJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); - Heating->Rooms.getOffsetTime(root); - - root.printTo(*ini); ini->println(); - PgmPrint("Free RAM: "); Serial.println(FreeRam());// part of sdFatUtil -} -void writeConfRoomsTemps(SdFile* ini, cHeating* Heating) { - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - Heating->Rooms.getOffsetTemp(root); - - root.printTo(*ini); ini->println(); -} -void writeConfRooms(SdFile* ini, cHeating* Heating) { - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - Heating->Rooms.getRooms(root); - - root.printTo(*ini); ini->println(); -} -void writeConfBurner(SdFile* ini, cHeating* Heating) { - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - Heating->Burner.getSP(root); - - root.printTo(*ini); ini->println(); -} -void writeConfBoiler(SdFile* ini, cHeating* Heating) { - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - Heating->Boiler.getSP(root); - - root.printTo(*ini); ini->println(); -} -void writeConfWarmWater(SdFile* ini, cHeating* Heating) { - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - Heating->WarmWater.getSP(root); + (Obj->*func)(root); root.printTo(*ini); ini->println(); } int writeConf( cHeating* Heating) { if ((!sderror)&&(!fileerror)) { - if (ini.open(iniFileName, O_CREAT | O_RDWR | O_TRUNC )) { + if (ini.open(ConfigFileName, O_CREAT | O_RDWR | O_TRUNC )) { + writeConfelement(&ini, &(Heating->Rooms), &cRooms::getOffsetTime); + writeConfelement(&ini, &(Heating->Rooms), &cRooms::getOffsetTemp); + writeConfelement(&ini, &(Heating->Rooms), &cRooms::getRooms); + writeConfelement(&ini, &(Heating->Burner),&cBurner::getSP); + writeConfelement(&ini, &(Heating->Boiler),&cBoiler::getSP); + writeConfelement(&ini, &(Heating->WarmWater),&cWarmWater::getSP); - writeConfRoomsTimes(&ini, Heating); - writeConfRoomsTemps(&ini, Heating); - writeConfRooms(&ini, Heating); - writeConfBurner(&ini, Heating); - writeConfBoiler(&ini, Heating); - writeConfWarmWater(&ini, Heating); - delay(500); - ini.close(); - return 1; + return ini.close(); } else { errorFile("ini.open create failed."); @@ -193,7 +97,7 @@ int readConfigLine(char* line, cHeating* Heating) { JsonObject& root = jsonBuffer.parseObject(line); if (!root.success()) { - Serial.println(F("parseObject() failed")); + Serial.println(F("Reading config: parseObject() failed.")); return 0; } @@ -210,38 +114,36 @@ int readConfigLine(char* line, cHeating* Heating) { if (posReturn>0) { Serial.print(posReturn); - //Serial.println(F("Success to read in object")); return posReturn; } - else - { - //Serial.println(F("Failed to read in object")); - return 0; - } + else return 0; } -int readConfig(cHeating* Heating) { +bool readConf(cHeating* Heating) { if ((!sderror)&&(!fileerror)) { char line[LineSize]; int n; int posReturn = 0 ; - if(sd.exists(iniFileName)){ - if (ini.open(iniFileName, O_RDWR )){ + if(sd.exists(ConfigFileName)){ + if (ini.open(ConfigFileName, O_RDWR )){ // read in standard parameters while ((n = ini.fgets(line,sizeof(line))) > 0){ - Serial.println(line); + Serial.println(line); // Debug Output posReturn = readConfigLine(line, Heating); } - ini.close(); + return ini.close(); } else { errorFile("Config file ini.open failed."); return 0; } } - return 1; + else { + Serial.println(F("No System.cfg file found on SDCard.\n Writing config to SDCard.")); + return writeConf(Heating); + } } else return 0; } @@ -297,82 +199,45 @@ void logPrintData(JsonObject& root ) { } } } - else { - file.print(","); - file.print(F("unknown data type")); - } + else file.print(F(",unknown data type")); } } -void logWriteRooms( cHeating* Heating, boolean bHeaders ) { - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - Heating->Rooms.getData(root); - if (bHeaders) logPrintHeader(root); - else logPrintData(root); -} -void logWriteHeating( cHeating* Heating, boolean bHeaders ) { - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - Heating->getData(root); - if (bHeaders) logPrintHeader(root); - else logPrintData(root); -} -void logWriteBurner( cHeating* Heating, boolean bHeaders ) { - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - Heating->Burner.getData(root); - if (bHeaders) logPrintHeader(root); - else logPrintData(root); -} -void logWriteBoiler( cHeating* Heating, boolean bHeaders ) { - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - - Heating->Boiler.getData(root); - if (bHeaders) logPrintHeader(root); - else logPrintData(root); -} -void logWriteWarmWater( cHeating* Heating, boolean bHeaders ) { +template +void logWriteelement(T* Obj, boolean bHeaders) { StaticJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.createObject(); - Heating->WarmWater.getData(root); + Obj->getData(root); if (bHeaders) logPrintHeader(root); else logPrintData(root); } - // Log a data record. void logWrite(boolean bHeaders, cHeating* Heating) { if ((!sderror)&&(!fileerror)) { - // Check SD Card - if (bHeaders){ - file.print(F("Time,")); - file.print(F("millis")); - } + if (bHeaders) file.print(F("Time,millis")); else { - file.print(TimeNow.year(), DEC); file.print('/'); - file.print(TimeNow.month(), DEC); file.print('/'); - file.print(TimeNow.day(), DEC); file.print(' '); - file.print(TimeNow.hour(), DEC); file.print(':'); - file.print(TimeNow.minute(), DEC); file.print(':'); + file.print(TimeNow.year(), DEC); file.print(F("/")); + file.print(TimeNow.month(), DEC); file.print(F("/")); + file.print(TimeNow.day(), DEC); file.print(F(" ")); + file.print(TimeNow.hour(), DEC); file.print(F(":")); + file.print(TimeNow.minute(), DEC); file.print(F(":")); file.print(TimeNow.second(), DEC); file.print(F(",")); file.print(millis()); } + // Check SD Card if (!file.sync() || file.getWriteError()) errorFile("write error"); } - + // Check again if SDCard was working to avoid that the program crashes upon missing sd card (removed during operation) or sd card error if ((!sderror)&&(!fileerror)) { - // Write data to file. Start with log time in micros. , cWarmWater* WarmWater - logWriteRooms(Heating, bHeaders); - logWriteHeating(Heating, bHeaders); - logWriteBurner(Heating, bHeaders); - logWriteBoiler(Heating, bHeaders); - logWriteWarmWater(Heating, bHeaders); + // Write data to file. Start with log time in micros. + logWriteelement(&(Heating->Rooms),bHeaders); + logWriteelement(Heating,bHeaders); + logWriteelement(&(Heating->Burner),bHeaders); + logWriteelement(&(Heating->Boiler),bHeaders); + logWriteelement(&(Heating->WarmWater),bHeaders); + logWriteelement(&(Heating->Solar),bHeaders); file.println(); @@ -382,16 +247,17 @@ void logWrite(boolean bHeaders, cHeating* Heating) { } -char fileName[13]; -char* genFile(void) { - +char* genFile(char* fileName) { int y = TimeNow.year(); int m = TimeNow.month(); int d = TimeNow.day(); + sprintf(fileName, "%02d", y); fileName[0] = fileName[2]; fileName[1] = fileName[3]; - sprintf(fileName+2, "%02d%02d00.CSV", m, d); + sprintf(fileName+2, "%02d", m); + sprintf(fileName+4, "%02d", d); + sprintf(fileName+6, "00.CSV"); const uint8_t BASE_NAME_SIZE = 6; while (sd.exists(fileName)) { @@ -404,14 +270,15 @@ char* genFile(void) { errorFile("Can't create file name"); } } - if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) errorFile("Failed file.open"); + if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) errorFile("Failed to create new log file."); return fileName; } void startLogging(cHeating* Heating) { if ((!sderror)&&(!fileerror)) { Serial.print(F("Logging to: ")); - Serial.println(genFile()); + char fileName[13]; + Serial.println(genFile(fileName)); logWrite(true, Heating); Serial.println(F("Type any character to stop logging.")); logging = true; @@ -423,12 +290,10 @@ void stopLogging() { if ((!sderror)&&(!fileerror)) { // Close file and stop. file.close(); - Serial.read(); Serial.println(F("Done")); logging = false; - Serial.println(F("Type any character to start logging")); } - else Serial.println(F("Failed to stop logging")); + else Serial.println(F("Failed to stop logging.")); } diff --git a/fileServer.h b/fileServer.h new file mode 100644 index 0000000..5f1098e --- /dev/null +++ b/fileServer.h @@ -0,0 +1,207 @@ +#ifndef FILESERVER_H +#define FILESERVER_H + +#include +#include +#include + + +Sd2Card *card; +SdVolume volume; +SdFile root; + +#define error(s) error_P(PSTR(s)) + +void error_P(const char* str) { + PgmPrint("error: "); + SerialPrintln_P(str); + if (card->errorCode()) { + PgmPrint("SD error: "); + Serial.print(card->errorCode(), HEX); + Serial.print(','); + Serial.println(card->errorData(), HEX); + } + while(1); +} + +void initFileServer(void) +{ + card = sd.card(); // if (!card.init(SPI_HALF_SPEED, 4)) error("card.init failed!"); + + // initialize a FAT volume + if (!volume.init(card)) error("vol.init failed!"); + + PgmPrint("Volume is FAT"); + Serial.println(volume.fatType(),DEC); + Serial.println(); + + if (!root.openRoot(&volume)) error("openRoot failed"); +} + +/* commands are functions that get called by the webserver framework + * they can read any posted data from client, and they output to the + * server to send data back to the web browser. */ +void helloCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) +{ + /* this line sends the standard "we're all OK" headers back to the + browser */ + server.httpSuccess(); + + /* if we're handling a GET or POST, we can output our data here. + For a HEAD request, we just stop after outputting headers. */ + if (type != WebServer::HEAD) + { + /* this defines some HTML text in read-only memory aka PROGMEM. + * This is needed to avoid having the string copied to our limited + * amount of RAM. */ + P(helloMsg) = "

Hello, World!

"; + /* this is a special form of print that outputs from PROGMEM */ + server.printP(helloMsg); + server.print("
  • Filesystem
  • "); + } +} + +/* commands are functions that get called by the webserver framework + * they can read any posted data from client, and they output to the + * server to send data back to the web browser. */ +void restCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) +{ + /* this line sends the standard "we're all OK" headers back to the + browser */ + server.httpSuccess(); + + if (type == WebServer::GET) + { + + + + } + + /* if we're handling a GET or POST, we can output our data here. + For a HEAD request, we just stop after outputting headers. */ + if (type != WebServer::HEAD) + { + /* this defines some HTML text in read-only memory aka PROGMEM. + * This is needed to avoid having the string copied to our limited + * amount of RAM. */ + P(helloMsg) = "

    This is the Arduheat RestAPI.

    "; + /* this is a special form of print that outputs from PROGMEM */ + server.printP(helloMsg); + } +} + +// #define WEBDUINO_SERVER_HEADER + +void httpNotFound(WebServer &server){ + P(failMsg) = + "HTTP/1.0 404 Bad Request" CRLF + "Content-Type: text/html" CRLF + "Server: Webduino/" WEBDUINO_VERSION_STRING CRLF CRLF + "

    File Not Found !

    "; + server.printP(failMsg); +} + + +void fsAccessCmd(WebServer &server, WebServer::ConnectionType type, char **url_path, char *url_tail, bool tail_complete) +{ + /* + Use the following to test + curl "192.168.1.80/fs" + curl "192.168.1.80/fs/TESTFILE.INO" + curl -X DELETE "192.168.1.80/fs/TESTFILE.INO" + Sources + - http://www.ladyada.net/learn/arduino/ethfiles.html + Improvements + - Expose a WebDav interface http://blog.coralbits.com/2011/07/webdav-protocol-for-dummies.html + */ + if(!tail_complete) server.httpServerError(); + //Only serve files under the "/fs" path + if(strncmp(url_path[0],"fs",3)!=0){ +// DEBUG_PRINT_PL("Path not found 404"); + httpNotFound(server); + return; + } + if(url_path[1]==0){ + // do an ls + server.httpSuccess(); + dir_t p; + root.rewind(); + server.println(""); + } + else{ + // access a file + SdFile file; + if (! file.open(&root, url_path[1], O_READ)) { + httpNotFound(server); + } + else { + if(type==WebServer::GET){ + server.httpSuccess("text/plain"); + + unsigned char c[WEBDUINO_OUTPUT_BUFFER_SIZE-2]; + int n=0; + //PgmPrint("Free RAM: "); Serial.println(FreeRam());// part of sdFatUtil + + while ((n = file.read(c,WEBDUINO_OUTPUT_BUFFER_SIZE-2)) > 0) { + server.write(c,n); + } + } + else if(type==WebServer::DELETE){ +// DEBUG_PRINT_PL("DELETE"); + server.httpSuccess(); +// card.remove(url_path[1]); + } + file.close(); + } + } +} + +#endif \ No newline at end of file