machines/shredder/asterix-pp/firmware/app.cpp
2023-11-12 21:43:05 +01:00

472 lines
10 KiB
C++

#include <Vector.h>
#include <Streaming.h>
#include <Arduino.h>
#include "app.h"
#include "features.h"
#include <MemoryFree.h>
//#ifdef MODBUS_BRIDGE
#include "ModbusBridge.h"
//#endif
static Addon *addonsArray[10];
#ifdef HAS_DIP
int dipSwitchPins[] = {48, 49};
#endif
short App::ok()
{
if (now - dt > 100)
{
// motor should spin
/*
if (vfd->direction &&
mLoad->lastIdle > MOTOR_MIN_DT &&
mLoad->dt > MOTOR_MIN_DT &&
mLoad->currentState == MotorLoad::MSTATE::NONE)
{
return E_VFD_RUN;
}
// motor should not spin
if (!vfd->direction &&
vfd->dt > MOTOR_MIN_DT &&
mLoad->lastLoad > MOTOR_MIN_DT * 4 &&
vfd->direction == VFD::DIRECTION::STOP &&
mLoad->currentState != MotorLoad::MSTATE::NONE)
{
return E_VFD_LOSS;
}
*/
dt = now;
}
return E_OK;
}
App::App() : Addon("APP", APP, 1 << STATE),
shredButton(ProximitySensor(CONTROLLINO_A15)),
#ifdef HAS_DIRECTION_SWITCH
dirSwitch(new DirectionSwitch()),
#endif
#ifdef ENCLOSURE_SENSOR
enclosureSensor(new EnclosureSensor()),
#endif
#ifdef HAS_VFD
vfd(new VFD()),
#endif
#ifdef MOTOR_HAS_TEMPERTURE
mHeat(new MotorTemperature()),
#endif
#ifdef HAS_MOTOR_IR_SENSOR
mSpeed(new MotorSpeed()),
#endif
#ifdef HAS_DIP
_dipSwitch(new _DipSwitch(2, dipSwitchPins)),
#endif
#ifdef CARTRIDGE_FULL_1
cartridgeFull(new CartridgeFull()),
#endif
#ifdef HAS_HOPPER_LOADED
#ifdef HOPPER_LOADED_1
hopperLoaded(new HopperLoaded(HOPPER_LOADED_0, HOPPER_LOADED_1)),
#else
hopperLoaded(new HopperLoaded(HOPPER_LOADED_0)),
#endif
#endif
#ifdef MOTOR_LOAD_PIN
mLoad(new MotorLoad(MOTOR_LOAD_PIN)),
#endif
#ifdef HAS_AUTOREVERSE
autoReverse(new AutoReverse(this)),
#endif
#ifdef HAS_PLUNGER
plunger(new Plunger(this, (AddonFnPtr)&App::plungerCB)),
#endif
#ifdef HAS_SERIAL
serialBridge(new PPSerial(Serial)),
#endif
#ifdef HAS_POWER
#if defined(POWER_0) && defined(POWER_1)
powerSwitch(new Power(POWER_0, POWER_1)),
#else
powerSwitch(new Power(POWER_0, POWER_0)),
#endif
#endif
#ifdef HAS_OP_MODE_SWITCH
#ifdef OP_MODE_ANALOG
opModeSwitch(new OperationModeSwitch(OP_MODE_1_PIN, 120, 60, 30)),
#else
opModeSwitch(new OperationModeSwitch(OP_MODE_1_PIN, OP_MODE_2_PIN, OP_MODE_3_PIN)),
#endif
#endif
#ifdef HAS_MODBUS_BRIDGE
modbusBridge(new ModbusBridge()),
#endif
shredStateLast(0),
shredCancelState(0)
{
//#if defined(MODBUS_BRIDGE) && defined(HAS_VFD)
// vfd->modbus = modbusBridge;
//#endif
/// modbusBridge->debug(&Serial);
}
#ifdef HAS_STATES
String App::state()
{
const int capacity = JSON_OBJECT_SIZE(6);
StaticJsonDocument<capacity> doc;
doc["0"] = id;
doc["1"] = _state;
doc["2"] = shredState;
doc["3"] = overloaded;
doc["4"] = hopperLoaded->ok();
doc["5"] = opModeSwitch->value();
Serial.println(analogRead(CONTROLLINO_A15));
return doc.as<String>();
}
#endif
short App::getAppState(short val)
{
return _state;
}
void (*resetFunction)(void) = 0; // Self reset (to be used with watchdog)
short App::setAppState(short newState)
{
switch (newState)
{
case App::APP_STATE::RESET:
{
_state = App::APP_STATE::STANDBY;
shredStateLast = 0;
shredCancelState = 0;
shredState = DONE;
plunger->reset();
vfd->stop();
resetFunction();
return;
}
case App::APP_STATE::STANDBY:
{
switch (_state)
{
case App::APP_STATE::SHREDDING:
{
shredCancelState = App::SHRED_STATE::INIT;
shredState = App::SHRED_STATE::CANCELLING;
plunger->reset();
break;
}
}
}
}
_state = newState;
}
void printMem()
{
Serial.print("mem: ");
Serial.print(freeMemory());
Serial.println('--');
}
short App::setup()
{
Serial.begin(DEBUG_BAUD_RATE);
Serial.println("Booting Firmware ...................... ");
addons.setStorage(addonsArray);
setup_addons();
#ifdef MEARSURE_PERFORMANCE
printPerfTS = 0;
addonLoopTime = 0;
bridgeLoopTime = 0;
#endif
debugTS = 0;
comTS = 0;
loopTS = 0;
shredState = 0;
overloaded = 0;
_state = 0;
jamCounter = 0;
bootTime = millis();
shredButton.setup();
/*
timer.every(5000, [](App *app) -> void {
printMem();
},
this);
*/
}
void App::loop_service()
{
#ifdef HAS_POWER
powerSwitch->on(POWER_PRIMARY);
#endif
// _loop_motor_manual();
}
void App::_loop_motor_manual()
{
loop_auto_reverse();
#if defined(HAS_DIRECTION_SWITCH) && defined(HAS_VFD)
uchar sw = this->dirSwitch->loop();
if (sw == 2)
{
this->vfd->fwd(true);
}
else if (sw == 1)
{
this->vfd->rev(true);
}
else
{
this->vfd->stop();
}
#endif
}
void App::loop_normal()
{
#ifdef HAS_HOPPER_LOADED
loop_auto_reverse();
if (hopperLoaded->ok())
{
powerSwitch->on(POWER_PRIMARY);
vfd->fwd(true);
plunger->plunge();
}
else
{
this->vfd->stop();
// plunger->home();
powerSwitch->off(POWER_PRIMARY);
}
#endif
}
void App::debug_mode_loop()
{
uchar s = addons.size();
for (uchar i = 0; i < s; i++)
{
Addon *addon = addons[i];
if (addon->hasFlag(LOOP))
{
addon->loop();
}
}
#if defined(HAS_POWER) && defined(HAS_HOPPER_LOADED)
uchar sw = this->dirSwitch->loop();
if (sw)
{
powerSwitch->on(POWER_PRIMARY);
}
if (sw == 2)
{
this->vfd->fwd(true);
}
else if (sw == 1)
{
this->vfd->rev(true);
}
else
{
this->vfd->stop();
powerSwitch->off(POWER_PRIMARY);
}
debug();
if (sw)
{
delay(LOOP_DELAY);
return;
}
///////////////////////////////////////////////////
if (hopperLoaded->ok())
{
powerSwitch->on(POWER_PRIMARY);
this->vfd->fwd(true);
delay(3000);
}
else
{
this->vfd->stop();
powerSwitch->off(POWER_PRIMARY);
}
delay(LOOP_DELAY);
#endif
}
short App::loop()
{
loop_addons();
loop_com();
timer.tick();
now = millis();
shredButton.loop();
if (now - bootTime < BOOT_DELAY)
{
return;
}
if (shredState == STUCK)
{
_error = E_STUCK;
setAppState(App::SHRED_STATE::CANCELLING);
return;
}
short error = ok();
if (error)
{
_error = error;
return;
}
#ifdef HAS_OP_MODE_SWITCH
short op = opModeSwitch->value();
op = 0;
switch (op)
{
//pos 1
case OP_DEBUG:
{
#ifdef HAS_POWER
// powerSwitch->on(POWER_PRIMARY);
#endif
break;
}
// pos2
case OP_NORMAL:
{
#ifdef HAS_POWER
// powerSwitch->on(POWER_PRIMARY);
// powerSwitch->on(POWER_SECONDARY);
#endif
loop_normal();
debug();
break;
}
//pos 0
case OP_NONE:
{
#ifdef HAS_POWER
// powerSwitch->off(POWER_PRIMARY);
#endif
// vfd->stop();
// plunger->stop();
if (hopperLoaded->ok() && _state != SHREDDING)
{
Serial.println("hopper loaded");
shred();
}
loopShred();
break;
}
case OP_SERVICE:
{
// loop_normal();
powerSwitch->on(POWER_PRIMARY);
vfd->rev(true);
break;
}
}
#endif
}
void App::loop_com()
{
if (millis() - comTS > 300)
{
#if defined(HAS_BRIDGE) && defined(HAS_SERIAL)
PPSerial::Message *msg = serialBridge->read();
if (msg)
{
switch (msg->verb)
{
case Bridge::EC_METHOD:
{
char *strings[3];
char *ptr = NULL;
byte index = 0;
ptr = strtok(msg->payload, ":");
while (ptr != NULL && index < 4)
{
strings[index] = ptr;
index++;
ptr = strtok(NULL, ":");
}
int id = atoi(strings[0]);
char *_method = strings[1];
SKeyVal *method = VSL::instance()->hasMethod(id, _method);
if (method)
{
int arg = atoi(strings[2]);
Addon *addon = (Addon *)method->instance;
AddonFnPtr ptr = method->mPtr;
short ret = (addon->*ptr)(arg);
if (TEST(msg->flags, Bridge::STATE))
{
#ifdef HAS_STATES
this->appState(0);
#endif
}
else if (TEST(msg->flags, Bridge::RECEIPT))
{
#ifdef BRIDGE_HAS_RESPONSE
const char *response = Bridge::CreateResponse(msg->id, 0, ret);
Serial.write(response);
#endif
}
if (TEST(msg->flags, Bridge::DEBUG))
{
Serial.println("Called command");
}
Serial.println("Called command");
Serial.println(addon->name);
Serial.println(_method);
}
else
{
VSL::instance()->debug();
if (TEST(msg->flags, Bridge::DEBUG))
{
Serial.print("Incoming message, cant find class & method ");
/*
Serial.print(_class);
Serial.print(":");
Serial.print(_method);
*/
Serial.print(_method);
Serial.print("\n");
}
}
break;
}
}
msg->payload = NULL;
}
#endif
comTS = millis();
}
}