// Feather esp8266 // Feather LCD // Feather motor controler #undef STASSID #undef STAPSK #define STASSID "hackaton" #define STAPSK "WifiLAAS2018" #ifndef STASSID #define STASSID "laas-capture" #define STAPSK "" #endif #if !CORE_MOCK && !defined(ARDUINO_ESP8266_ADAFRUIT_HUZZAH) #error select board Adafruit Feather HUZZAH ESP8266 #endif #define NAME "PousseSeringue" #define OLED_HW Adafruit128x32 #define BTNA 0 // gpio A adafruit feather oled 128x32 #define BTNB 16 // gpio B adafruit feather oled 128x32 #define BTNC 2 // gpio C adafruit feather oled 128x32 #define OLED_I2C 0x3c // adafruit feather oled 128x32 #define COURSE 14 // (d5) fin de course #define BAUDRATE 115200 // should this be configurable through a GUI ? #define MOTOR_STEPS 200 // 17HS19 #define MICROSTEPS_CONF 16 // hardware configuration: A4988's MS1,MS2,MS3 tied up #include <ESP8266WiFi.h> // internal, wifi library #include <DNSServer.h> // internal, access point mode #include <Schedule.h> #include <interrupts.h> // InterruptLock using InterruptLock = esp8266::InterruptLock; #if !CORE_MOCK #include <SSD1306AsciiWire.h> // https://github.com/greiman/SSD1306Ascii SSD1306AsciiWire oled; #endif // !CORE_MOCK #include "motor.h" #include "Debouncer.h" // local, debouncer, short counter, long detector #include "syringe.h" #include "syringefilled.h" #include "cli.h" #include "common.h" #include "fs.h" enum class Modes { TURN, TURNSetup, RPM, RPMSetup, INFO }; const char* msgHeader = MSGHEADER; Debouncer bA, bB, bC; ButtonFast bEmergency; constexpr int DNS_PORT = 53; DNSServer dnsServer; char ssid[65]; Modes mode = Modes::RPM; int move1 = 0; // TURN mode constexpr float step2deg = 360.0 / MOTOR_STEPS / MICROSTEPS_CONF; // deg for 1 step int steps = MOTOR_STEPS * MICROSTEPS_CONF / 4; // =1/4 turn // RPM mode int stepsPerSeconds = MOTOR_STEPS * MICROSTEPS_CONF / 2; float stepsPerSeconds2rpm () { return 60.0 * stepsPerSeconds / (MOTOR_STEPS * MICROSTEPS_CONF); } int rpming = 0; // -1, 0 or +1 // single syringe global instance SyringeFilled syringefilled(stepper); Cli console(syringe_filled); const char* oledMode () { switch (mode) { case Modes::TURN: return "TURN"; case Modes::TURNSetup: return "TURN Setup"; case Modes::RPM: return "RPM"; case Modes::RPMSetup: return "RPM Setup"; case Modes::INFO: return "Informations"; } return "mode?"; } void oledPrintMode () { #if !CORE_MOCK oled.printf("\n mode: %s", oledMode()); #endif // !CORE_MOCK } void oledRedisplay () { #if !CORE_MOCK oled.clear(); switch (mode) { case Modes::TURN: oled.printf("<%g dR <<360 dR", steps * step2deg); oledPrintMode(); oled.printf("\n"); oled.printf("\n<%g dL <<360 d ", steps * step2deg); break; case Modes::TURNSetup: oled.printf("<+1step << +1/16T"); oledPrintMode(); oled.printf("\n steps:%d (%g d)", steps, steps * step2deg); oled.printf("\n<-1step << -1/16T"); break; case Modes::RPM: oled.printf("<stop <<start"); oledPrintMode(); oled.printf("\n %g rpm", stepsPerSeconds2rpm()); oled.printf("\n<stop <<start"); break; case Modes::RPMSetup: oled.printf("<+1s/s <<+1/4T/s"); oledPrintMode(); oled.printf("\ns/s:%d RPM:%g", stepsPerSeconds, stepsPerSeconds2rpm()); oled.printf("\n<-1s/s <<-1/4T/s"); break; case Modes::INFO: oled.printf("IP:%s", WiFi.localIP().toString().c_str()); uint8_t mac[6]; WiFi.macAddress(mac); oled.printf("\n%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); oled.printf("\nAPSSID & hostname:"); oled.printf("\n%s", ssid); break; default: oled.printf("?"); } #endif // !CORE_MOCK } void changeMode () { switch (mode) { case Modes::TURN: mode = Modes::TURNSetup; break; case Modes::TURNSetup: mode = Modes::RPM; break; case Modes::RPM: mode = Modes::RPMSetup; break; case Modes::RPMSetup: mode = Modes::INFO; break; case Modes::INFO: mode = Modes::TURN; break; } oledRedisplay(); } // button status management, per mode void buttons (int shortp, int longp) { // log debug on serial console Serial.printf("button change: short:%d long:%d\n", shortp, longp); syringe_filled.stop(); auto oldrpming = rpming; rpming = 0; // anything stops rpming switch (mode) { case Modes::TURN: Serial.printf("move1: %d -> ", move1); move1 += shortp * steps; move1 += longp * MOTOR_STEPS * MICROSTEPS_CONF; // long: one turn Serial.printf("%d\n", move1); #if !CORE_MOCK cli(); stepper.moveTo(move1); sei(); #endif // !CORE_MOCK break; case Modes::TURNSetup: steps += shortp; steps += longp * MOTOR_STEPS * MICROSTEPS_CONF / 16; oledRedisplay(); #if !CORE_MOCK cli(); stepper.setCurrentPosition(0); sei(); #endif // !CORE_MOCK break; case Modes::RPM: if (longp) { rpming = longp? (oldrpming > 1? -1: 1): 0; if (rpming) { #if !CORE_MOCK cli(); stepper.setSpeed(stepsPerSeconds); sei(); #endif // !CORE_MOCK Serial.printf("StepPerSecs=%d (%g RPM)\n", stepsPerSeconds, stepsPerSeconds2rpm()); //stepper.setMaxSpeed(400); } } break; case Modes::RPMSetup: if ((stepsPerSeconds += longp * MOTOR_STEPS * MICROSTEPS_CONF / 16) < 1) stepsPerSeconds = 1; if ((stepsPerSeconds += shortp) < 1) stepsPerSeconds = 1; oledRedisplay(); break; default: Serial.printf("press not handled in buttons()\n"); } } #if !CORE_MOCK IRAM_ATTR void stepperRun () { stepper.runSpeed(); } #endif // !CORE_MOCK void setup() { Serial.begin(BAUDRATE); Serial.setDebugOutput(true); Serial.println(); Serial.println("Booting"); #if !CORE_MOCK Wire.begin(); Wire.setClock(400000L); #endif // !CORE_MOCK delay(500); #if !CORE_MOCK // initialize hardware oled.begin(&OLED_HW, OLED_I2C); oled.setFont(System5x7); oled.setScrollMode(SCROLL_MODE_AUTO); oled.clear(); oled.println("Booting...\n"); syringe_filled.set_physical(MOTOR_STEPS * MICROSTEPS_CONF, M5_MMREV, /*forwardClockWise*/false); pinMode(SLEEP, OUTPUT); digitalWrite(SLEEP, 0); #endif // !CORE_MOCK #if 1 uint8_t mac[6]; WiFi.macAddress(mac); sprintf(ssid, NAME "-%02x%02x%02x", mac[3], mac[4], mac[5]); #if 1 if (strlen(STASSID) > 0) { WiFi.setPhyMode(WIFI_PHY_MODE_11G); WiFi.mode(WIFI_STA); //WiFi.softAP(ssid); WiFi.hostname(ssid); WiFi.begin(STASSID, STAPSK); } #endif // redirect everything to my AP ip: dnsServer.setErrorReplyCode(DNSReplyCode::NoError); dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); Serial.printf("AP: ssid='%s' (no password)\n", ssid); Serial.printf("---> Connect to this SSID '%s' and head to http://%s <---\n", ssid, WiFi.softAPIP().toString().c_str()); Serial.printf("---> Within SSID %s, connect to http://%s <----\n", STASSID, ssid); Serial.printf("---> (or read IP address on screen)\n"); Serial.printf("connecting to ssid '%s'\n", STASSID); web_setup(); #endif //0 pinMode(BTNA, INPUT_PULLUP); pinMode(BTNB, INPUT_PULLUP); pinMode(BTNC, INPUT_PULLUP); pinMode(COURSE, INPUT_PULLUP); oledRedisplay(); #if 1 #if !CORE_MOCK #ifdef ISR_ATTR timer1_isr_init(); timer1_enable(TIM_DIV1, TIM_EDGE, TIM_LOOP); timer1_attachInterrupt(stepperRun); timer1_write(microsecondsToClockCycles(100)); schedule_recurrent_function_us([](){ if (stepper.distanceToGo()) { InterruptLock lock; stepper.computeNewSpeed(); } return true; }, 1); #else #warning =================================================================================================== #warning "Please use the ISR version of AccelStepper (git clone -b isr https://github.com/d-a-v/AccelStepper)" #warning =================================================================================================== schedule_recurrent_function_us([](){ stepperRun(); return true; }, 1); #endif #endif #endif // check if emergency button is pressed at boot time syringe_filled.set_emergency(!!digitalRead(COURSE)); if (fs_init(msgHeader, &Serial)) { Serial.printf("FS initialized, dav http server started\n"); } else { Serial.printf("FS: could not be started\n"); } //Json syringe_filled.read_Json(); syringe.read_Json(); } void loop() { if (bEmergency.update(digitalRead(COURSE))) { syringe_filled.manage_emergency(bEmergency.pressed(), !!stepper.distanceToGo()); Serial.printf("%sEmergency=%u (remaining steps = %lu)\n", msgHeader, bEmergency.pressed(), stepper.distanceToGo()); } //Code pour les boutons de l'écran #if CORE_MOCK bool changeA = false; bool changeB = false; bool changeC = false; #else // button management bool changeA = bA.update(!digitalRead(BTNA)); bool changeB = bB.update(!digitalRead(BTNB)); bool changeC = bC.update(!digitalRead(BTNC)); #endif if (changeA) { // button values (::shortCount(), ::longState()) // are not cleared until they are read int shortA = bA.shortCount(); bool longA = bA.longState(); buttons(shortA, longA); } if (changeC) { int shortC = bC.shortCount(); bool longC = bC.longState(); buttons(-shortC, -longC); } if (changeB) { int shortB = bB.shortCount(); bool longB = bB.longState(); if (longB) changeMode(); (void)shortB; } console.loop(Serial); web_loop(); dnsServer.processNextRequest(); // access-point redirection }