#include "syringefilled.h" ISRStepper stepper(AccelStepper::DRIVER, STEP, DIR); SyringeFilled syringe_filled(stepper); //CONSTRUCTORS SyringeFilled :: SyringeFilled (MotorHardware_t& stepper): Motor(stepper) { set_exchange_throughput_uL_per_sec(1); set_exchange_volume_mL(1); set_remaining_volume_mL(1); set_push(true); set_name_syringe("BD_10mL"); set_clockwise_equals_push(true); set_lead_screw_pitch_mm(0.7); set_syringe_pump_length_mm(50); set_microstep_configuration(16); set_motor_steps(200); set_emergency(false); } //SET METHODS void SyringeFilled :: set_exchange_throughput_uL_per_sec(float exchange_throughput_uL_per_sec) { syringe_filled_json["exchange_throughput_uL_per_sec"] = exchange_throughput_uL_per_sec; } void SyringeFilled :: set_exchange_volume_mL(float exchange_volume_mL) { syringe_filled_json["exchange_volume_mL"] = exchange_volume_mL; } void SyringeFilled :: set_remaining_volume_mL(float remaining_volume_mL) { syringe_filled_json["remaining_volume_mL"] = remaining_volume_mL; } void SyringeFilled :: set_push(bool push) { syringe_filled_json["push"] = push; } void SyringeFilled :: set_name_syringe(String name_syringe) { syringe_filled_json["name_syringe"] = name_syringe; } void SyringeFilled :: set_clockwise_equals_push(bool clockwise_equals_push) { syringe_filled_json["clockwise_equals_push"] = clockwise_equals_push; } void SyringeFilled :: set_syringe_pump_length_mm(float syringe_pump_length_mm) { syringe_filled_json["syringe_pump_length_mm"] = syringe_pump_length_mm; } void SyringeFilled :: set_lead_screw_pitch_mm(float lead_screw_pitch_mm) { syringe_filled_json["lead_screw_pitch_mm"] = lead_screw_pitch_mm; } void SyringeFilled :: set_microstep_configuration(int microstep_configuration) { syringe_filled_json["microstep_configuration"] = microstep_configuration; } void SyringeFilled :: set_motor_steps(int motor_steps) { syringe_filled_json["motor_steps"] = motor_steps; } void SyringeFilled :: set_emergency(bool emergency) { syringe_filled_json["emergency"] = emergency; } //GET METHODS float SyringeFilled :: get_exchange_throughput_uL_per_sec() { return syringe_filled_json["exchange_throughput_uL_per_sec"].as<float>(); } float SyringeFilled :: get_exchange_volume_mL() { return syringe_filled_json["exchange_volume_mL"].as<float>(); } float SyringeFilled :: get_remaining_volume_mL() { return syringe_filled_json["remaining_volume_mL"].as<float>(); } bool SyringeFilled :: get_push() { return syringe_filled_json["push"].as<bool>(); } String SyringeFilled :: get_name_syringe() { return syringe_filled_json["name_syringe"].as<String>(); } bool SyringeFilled :: get_clockwise_equals_push() { return syringe_filled_json["clockwise_equals_push"].as<bool>(); } float SyringeFilled :: get_syringe_pump_length_mm() { return syringe_filled_json["syringe_pump_length_mm"].as<float>(); } float SyringeFilled :: get_lead_screw_pitch_mm() { return syringe_filled_json["lead_screw_pitch_mm"].as<float>(); } int SyringeFilled :: get_microstep_configuration() { return syringe_filled_json["microstep_configuration"].as<int>(); } int SyringeFilled :: get_motor_steps() { return syringe_filled_json["motor_steps"].as<int>(); } bool SyringeFilled :: get_emergency() { return syringe_filled_json["emergency"].as<bool>(); } const StaticJsonDocument<200>& SyringeFilled :: get_syringe_filled_data() { return syringe_filled_json; } //CONVERSIONS StaticJsonDocument<200> syringe_filled_json; //200 = RAM allocated to this document float SyringeFilled::volume_mL_to_distance_mm(float volume_mL) /*** -Argument : Volume in mL. -Return : The distance to cover associated in mm. -Action : / ***/ { float volume_mm3 = 1000 * volume_mL; //mL->mm3 float piston_radius_mm = (syringe.get_syringe_database()[syringe_filled_json[syringe_filled.get_name_syringe()]]["internal_diameter"]).as<int>()/2; float piston_surface_mm2 = M_PI * piston_radius_mm * piston_radius_mm; float distance_mm = volume_mm3 / piston_surface_mm2; return distance_mm; } float SyringeFilled::distance_mm_to_volume_mL(float distance_mm) /*** -Argument : distance in mm. -Return : The volume associated in mL. -Action : / ***/ { float piston_radius_mm = (syringe.get_syringe_database()[syringe_filled_json[syringe_filled.get_name_syringe()]]["internal_diameter"]).as<int>()/2; float piston_surface_mm2 = M_PI * piston_radius_mm * piston_radius_mm; float volume_mm3 = distance_mm * piston_surface_mm2; float volume_mL = volume_mm3 / 1000; return volume_mL; } //MOVEMENTS bool SyringeFilled :: is_running () //return true if the motor is currently running /*** -Argument : / -Return : A boolean to indicates if the motor is running. -Action : / ***/ { return motor_is_running(); } void SyringeFilled :: start_exchange() /*** -Argument : / (We're using informations stored in the class itself) -Return : / -Action : Move the syringe filled to deliver or recover the volume expected ***/ { //Conversions of volumes float exchange_volume_mm3 = get_exchange_volume_mL()*1000; //Find the section float radius = syringe.get_syringe_database()[syringe_filled_json["name_syringe"]]["internal_diameter_mm"]; float section_mm2 = M_PI*radius*radius; set_speed_mm_per_sec(get_exchange_throughput_uL_per_sec()/section_mm2); if (get_push()) //If we want to deliver some liquid { move_mm_limit_switch(_clockwise_equals_push? exchange_volume_mm3/section_mm2: -exchange_volume_mm3/section_mm2); } else //If we want to recover some liquid { move_to_mm_limit_switch(_clockwise_equals_push? -exchange_volume_mm3/section_mm2: exchange_volume_mm3/section_mm2); } } void SyringeFilled :: go_to_zero() /*** -Argument : / (We're using informations stored in the class itself) -Return : / -Action : Move the syringe filled to deliver or recover the volume expected ***/ { if (get_emergency()) { return; } else { set_speed_mm_per_sec(-1); reset_position(); move_to_mm_limit_switch(_clockwise_equals_push? -200: 200); } } //LIMIT SWITCH void SyringeFilled::manage_emergency (bool pressed, bool at_zero) /*** -Argument : two boolean to know the position of the limit switch and if we are a the zero -Return : / -Action : Manage different emergency cases (move the pump if necessary) ***/ { if (pressed) //if the limit switch is pressed { set_emergency(true); write_Json(); if (at_zero) //if we are at te initialisation place { run_from_emergency(); } else { Serial.printf("EMERGENCY: Pressed by someone\n"); } } else { set_emergency(false); write_Json(); Serial.printf("EMERGENCY: released\n"); stop(); reset_position(); //zero is here again Serial.printf("ZERO: reset\n"); } } void SyringeFilled::run_from_emergency() /*** -Argument : / -Return : / -Action : Move the pump slowly until the limit switch is released ***/ { if (get_emergency()) { Serial.printf("EMERGENCY: running away slowly\n"); set_speed_mm_per_sec(-1); reset_position(); move_to_mm_limit_switch(_clockwise_equals_push? 200: -200); } } //CONFIGURATION bool SyringeFilled :: check_configuration() /*** -Argument : / -Return : A boolean true(everything is fine), false(at least one attribut is'nt correct) -Action : Check if the values are correct. ***/ { if (syringe.get_syringe_database()[syringe_filled_json["name_syringe"]]["internal_diameter"] <= 0) return false; if (get_exchange_volume_mL() < 0) return false; if (get_remaining_volume_mL() <= 0) return false; if (get_exchange_throughput_uL_per_sec() <= 0) return false; return true; } void SyringeFilled :: show_configuration() /*** -Argument : / -Return : / -Action : Displays syringe filled attributes ***/ { //Motor::show_configuration(); auto step = where_step(); Serial.printf("# push clockwise: %s\n" //"# diameter: %g mm\n" //"# capacity: %g uL\n" "# rate: %g uL/s = %g mm/s\n" "# volume remaining: %g uL (target)\n" "# direction: %s\n" "# emergency: %d\n" "# current position: %g mm\n", get_clockwise_equals_push()? "yes": "no", //syringe.get_syringe_database()[syringe_filled_json["name_syringe"]]["internal_diameter"], //syringe.get_syringe_database()[syringe_filled_json["name_syringe"]]["total_volume_mL"], get_exchange_throughput_uL_per_sec(), volume_mL_to_distance_mm(get_exchange_throughput_uL_per_sec()), get_remaining_volume_mL(), get_push()? "infuse": "withdraw", get_emergency(), step_to_mm(step)); } //JSON //Example : File f = ({ InterruptLock lock; filesystem->open(filename, "w"); }); and the f becomes the argument input_stream //useful in web.cpp via a button to save data void SyringeFilled :: write_Json () /*** -Argument : input_stream (= serial or a file) -Return : / -Action : Save the Json structure in a file (convert it to text) ***/ { InterruptLock lock; //useful thanks to its constructor (so a is not visible in the code) File f = filesystem->open("syringe_filled_json", "w"); //Creation du fichier size_t a = serializeJson(syringe_filled_json, f); Serial.println("Taille de syringe_filled_json"); Serial.println(a); f.close(); } //useful in setup of pousse-seringue.cpp(to check at the begining what has already been saved) and web.cpp via a button to recover data void SyringeFilled :: read_Json () /*** -Argument : output_stream (= serial or a file) -Return : / -Action : Check if a Json Document alredy exist or not, if it does it is load ***/ { InterruptLock lock; //useful thanks to its constructor (so a is not visible in the code) File f = filesystem->open("syringe_filled_json", "r"); //Creation du fichier deserializeJson(syringe_filled_json, f); }