#include #include #include #include #include #include #include #include #ifdef __AVR__ #include #endif #define PIN_PIXELS 4 #define NUM_PIXELS 6 #define TUBES_COUNT 6 // Web server configuration const char* ssid = "ESP_ping"; const char* password = "pong"; const char* PARAM_INPUT_1 = "output"; const char* PARAM_INPUT_2 = "state"; AsyncWebServer server(80); CRGB leds[NUM_PIXELS]; struct pin { int id; int is_extender; }; enum state { STATE_NONE, STATE_INIT, STATE_OK, STATE_MOVING, STATE_ERROR }; struct tube { uint16_t height; uint16_t target; struct { Servo hard; struct pin pin; int last_angle; int equilibrium; } servo; struct { VL53L0X hard; struct pin gpio; struct pin xshut; } tof; enum state state; int id; }; enum mode { NONE, SINUS, MAX_MODE, }; enum mode current_mode = NONE; const struct pin all_xshuts[6] = { {.id = 7, .is_extender = 1}, {.id = 0, .is_extender = 1}, {.id = 1, .is_extender = 1}, {.id = 6, .is_extender = 1}, {.id = 5, .is_extender = 1}, {.id = 4, .is_extender = 1} }; const struct pin all_servos[6] = { {.id = 32, .is_extender = 0}, {.id = 33, .is_extender = 0}, {.id = 18, .is_extender = 0}, {.id = 27, .is_extender = 0}, {.id = 14, .is_extender = 0}, {.id = 19, .is_extender = 0} }; struct tube all_tubes[6]; const float minHeight = 50; const float maxHeight = 445; int minServo = 0; int maxServo = 180; Adafruit_PCF8574 pcf8574; void scan_I2c(){ byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); nDevices++; } else if (error==4) { Serial.print("Unknow error at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); } } if (nDevices == 0) { Serial.println("No I2C devices found\n"); } else { Serial.println("done\n"); } delay(5000); } uint16_t mesureHeight(int i){ uint16_t h = all_tubes[i].tof.hard.readRangeContinuousMillimeters(); //Serial.printf("measure: %d\n", h); if (h > 2000){ //Serial.printf("returning height\n"); return all_tubes[i].height; } else { //Serial.printf("returning: %d\n", h); return h; } } void change_pin(struct pin pin, int state) { if (pin.is_extender) { pcf8574.digitalWrite(pin.id, state); } else { digitalWrite(pin.id, state); } } void changePinMode(struct pin pin, int state) { if (pin.is_extender) { pcf8574.pinMode(pin.id, state); } else { pinMode(pin.id, state); } } void change_led(int id) { Serial.print("Changing led color of #"); Serial.println(id); int led_id = NUM_PIXELS - 1 - id; switch (all_tubes[id].state) { case STATE_INIT: { leds[led_id] = CRGB(255, 210, 70); break; } case STATE_OK: { leds[led_id] = CRGB::Blue; break; } case STATE_MOVING: { float ratio = (all_tubes[id].height) / (maxHeight); if (ratio > 1.0) { ratio = 1.0; } else if (ratio < 0.0f) { ratio = 0.0; } leds[led_id] = CHSV((int) (ratio * 128.0 + (1 - ratio) * 240.0) - 90, 255, 255); break; } case STATE_ERROR: { leds[led_id] = CRGB::Red; break; } default: { leds[led_id] = CRGB::Black; break; } } FastLED.show(); } // Web Server configuration const char index_html[] PROGMEM = R"rawliteral( Balle Ping Pong flottante

Balle Ping Pong flottante

%BUTTONPLACEHOLDER%
)rawliteral"; String processor(const String& var){ //Serial.println(var); if(var == "BUTTONPLACEHOLDER"){ String buttons = ""; buttons += "
"; buttons += "
"; buttons += "
"; buttons += "
"; buttons += "
"; buttons += "
"; return buttons; } return String(); } void wifisetup() { Serial.print("Setting AP (Access Point)…"); WiFi.softAP(ssid); IPAddress IP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(IP); // Route for root / web page server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", index_html, processor); }); // Send a GET request to /update?output=&state= server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) { if (current_mode != NONE) { request->send(401, "text/plain", "NOK"); return; } int inputMessage1; int inputMessage2; // GET input1 value on /update?output=&state= if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) { inputMessage1 = request->getParam(PARAM_INPUT_1)->value().toInt(); inputMessage2 = request->getParam(PARAM_INPUT_2)->value().toInt(); Serial.printf("Received web command: %d %d\n", inputMessage1, inputMessage2); all_tubes[inputMessage1].target = inputMessage2; } else { // Error inputMessage1 = -1; inputMessage2 = -1; } Serial.print("GPIO: "); Serial.print(inputMessage1); Serial.print(" - Set to: "); Serial.println(inputMessage2); request->send(200, "text/plain", "OK"); }); // Send a GET request to /update?output=&state= server.on("/switch_mode", HTTP_GET, [] (AsyncWebServerRequest *request) { current_mode = static_cast(NONE + (current_mode + 1) % MAX_MODE); request->send(200, "text/plain", "OK"); }); // Start server server.begin(); } // Tube configuration void setup_all_tofs() { // Reset all TOFs by turning them up and low for (int i = 0; i < TUBES_COUNT; ++i) { changePinMode(all_xshuts[i], OUTPUT); change_pin(all_xshuts[i], LOW); } for (int i = 0; i maxHeight) { Serial.printf("%dmm %d°\n",h, angle); ++angle; if (angle > maxServo) { all_tubes[i].state = STATE_ERROR; Serial.printf("Tube %d HS ! (moteur ne tourne pas ?)\n", i); change_led(i); return; } all_tubes[i].servo.hard.write(angle); delay(300); h = mesureHeight(i); } angle -= 5; all_tubes[i].servo.last_angle = angle; all_tubes[i].servo.hard.write(all_tubes[i].servo.last_angle); all_tubes[i].servo.equilibrium = angle; Serial.printf("Tube %d calibrated.\n", i); all_tubes[i].state = STATE_OK; change_led(i); } void setup_servos() { for (int i = 0; i < TUBES_COUNT; ++i) { all_tubes[i].servo.pin = all_servos[i]; all_tubes[i].servo.hard.attach(all_tubes[i].servo.pin.id); all_tubes[i].servo.hard.write(minServo); } } // Helper functions auto heightRatio(uint16_t h){ float height = h; return max(min(1.0f, (height - minHeight) / (maxHeight-minHeight)), 0.0f); } int servoPosition(float ratio){ return (ratio * maxServo) + ((1-ratio) * minServo); } void setup() { Serial.begin(115200); // Starts the serial communication while (! Serial) { delay(1); } FastLED.addLeds(leds, NUM_PIXELS); FastLED.setBrightness(255); for (int i = 0; i < NUM_PIXELS; ++i) { leds[i] = CRGB::Black; } FastLED.show(); setup_servos(); delay(5000); setup_servos(); delay(5000); Wire.begin(); if (!pcf8574.begin(0x38, &Wire)) { Serial.println("Couldn't find PCF8574"); while (1); } for (uint8_t p=0; p<8; p++) { pcf8574.pinMode(p, OUTPUT); } scan_I2c(); Serial.println("PCF8574 is OK"); wifisetup(); setup_all_tofs(); for (int i = 0; i %f\n", all_tubes[i].height, all_tubes[i].target, angle); } step += 0.1; } for (int i = 0; i 5.0f) { angle = all_tubes[i].servo.equilibrium + (diff * 1 / (7)); } else if (abs(diff) > 2.0f) { angle = all_tubes[i].servo.equilibrium + (diff * 1 / (7)); } else { angle = all_tubes[i].servo.equilibrium; } //Serial.printf("%d - %d = %d => %d\n", all_tubes[i].height, all_tubes[i].target, diff, angle); all_tubes[i].servo.last_angle = min(max(angle, minServo), maxServo); all_tubes[i].servo.hard.write(all_tubes[i].servo.last_angle); } // Serial.println(); //Serial.printf("%d %d %d %d %d %d \n", val0, val1, val2, val3, val4, val5); // Serial.printf("%d %d %d \n", val3, val4, val5); //Serial.printf("%d %d \n", val0, val1); delay(5); }