4 minute read
Programmierung des Esp32 ( Sender)
Das programmieren war nur Dank Thomas möglich ( ein riesiges Dankeschön !)
Funktionsprinzip Sender
Als Basis benutzen wir das QT PY ESP32-S2, Die Wahl viel auf diese Board wegen des Formfaktors und der Möglichkeit einer direkten WIFI Verbindung zu dem Empänger ( XIAO32-S2) herzustellen. Angeschlsossen Elemente bestehen in 3 Drucksensoren und einem Gyrosensor sowie einem LiPO Akku.
Der Code lässt sich in mehrere Hauptbereiche gliedern, Bibliotheken und globale Variablen: Am Anfang des Codes werden verschiedene Bibliotheken importiert, die für die Kommunikation mit den Sensoren und die Kommunikation über ESP-NOW benötigt werden. Es werden auch einige globale Variablen und Strukturen erstellt, die später im Code verwendet werden.
Funktionen zur Sensorwertanpassung: mapAndAdjustPressureSensorValues und mapAndAdjustAccelerometerSensorValues sind Funktionen, die zum Anpassen und Skalieren der von den Sensoren gelesenen Werte dienen.
ESP-NOW Rückruffunktion: OnDataSent ist eine Funktion, die aufgerufen wird, nachdem ein Datenpaket gesendet wurde. Sie gibt Informationen darüber aus, ob das Senden erfolgreich war oder nicht.
Setup-Funktion: Die setup Funktion wird einmal ausgeführt, wenn das Programm startet. Sie initialisiert das Serial-Interface, das WiFi-Interface, registriert die ESP-NOW Rückruffunktion, fügt den Empfänger als Peer hinzu und initialisiert den Beschleunigungssensor.
Loop-Funktion: Die loop Funktion wird immer wieder ausgeführt, solange das Gerät läuft. In dieser Funktion werden die Sensorwerte gelesen, in das richtige Format gebracht und über ESP-NOW an den Empfänger gesendet.
Der allgemeine Ablauf des Programms ist also wie folgt:
Beim Start wird die setup Funktion ausgeführt, um alles für den Betrieb vorzubereiten.
Danach wird die loop Funktion immer wieder ausgeführt. Bei jedem Durchlauf liest sie die Sensorwerte, bereitet die Daten für das Senden vor und sendet sie dann über ESP-NOW.
Jedes Mal, wenn Daten gesendet werden, wird die OnDataSent Funktion aufgerufen, um das Ergebnis des Sendevorgangs anzuzeigen.
}
*/
Based on: Seeedstudio XIAO ESP32-C3
The reason we chose this board, was it`s small formfactor and its external antenna for better signal transmission. Based on the following examples: https://dronebotworkshop.com/esp-now/ https://github.com/un0038998/ESPNOW_Transmitter_Receiver
/* ESP-NOW GLOBALS *********************************************************/
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
Adafruit_MPU6050 mpu;
/* ESP-NOW GLOBALS *********************************************************/
#include <esp_now.h>
#include <WiFi.h>
// REPLACE WITH YOUR RECEIVER MAC Address
// QT Py ESP32-S2 Receiver MAC-Adress: D4:F9:8D:71:1E:9C
// QT Py ESP32-S2 Receiver MAC-Adress: D4:F9:8D:70:88:98 (Clone) uint8_t receiverMacAddress[] = { 0xD4, 0xF9, 0x8D, 0x71, 0x1E, 0x9C }; struct PacketData { byte ForwardForce0; byte RightForce1; byte LeftForce2; byte UpDownForce3; // int RightForce4;
}; PacketData data;
// Pins for the force sens ors
// const int ForwardForceSensor1 = A0; // const int UpForceSensor1 = A1; // ... not sure if necessary to define this
// Force threshold const int ForceThreshold = 500;
/* GET Force Sensor Data FUNCTION ******************************************************/ int mapAndAdjustPressureSensorValues(int value, bool reverse) {
// Check force on sensor if (value > ForceThreshold) {
Serial.print(„Pressure Sensor Value:“); Serial.println(value); return value; delay(50);
}
/* GET Accelerometer Sensor Data FUNCTION ********************************************* *********/ int mapAndAdjustAccelerometerSensorValues(int value, bool reverse)
{ if (value >= 4)
{ value = map(value, 4.0, 10.0, 127, 254);
} else if (value <= -4)
{ value = (value == 0 ? 0 : map(value, -4.0, -10.0, 127, 0));
} else
{ value = 127;
} if (reverse)
{ value = 254 - value;
} Serial.println(value); return value;
}
/* ESP-NOW CALLBACK *********************************************************/
// callback when data void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.print(„\r\nLast Packet Send Status:\t „); Serial.println(status); Serial.println(status == ESP_NOW_SEND_SUCCESS ? „Message sent“ „Message failed“);
}
/* SETUP FUNCTION *************************************************************/ void setup() {
// Initialize the force sensors as input // pinMode(ForwardForceSensor1, INPUT); // ... not sure if necessary to define this delay(50); Serial.begin(115200); WiFi.mode(WIFI_STA);
// Init ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println(„Error initializing ESP-NOW“); return; } else { Serial.println(„Succes: Initialized ESP-NOW“); } esp_now_register_send_cb(OnDataSent);
// Register peer esp_now_peer_info_t peerInfo; memcpy(peerInfo.peer_addr, receiverMacAddress, 6); peerInfo.channel = 0; peerInfo.encrypt = false;
// Add peer if (esp_now_add_peer(&peerInfo) != ESP_OK) { Serial.println(„Failed to add peer“); return;
} else { Serial.println(„Succes: Added peer“); }
// Initialize Accelerometer if (!mpu.begin()) { Serial.println(„Failed to find MPU6050 chip“); while (1) { delay(10);
}
} Serial.println(„MPU6050 Found!“); mpu.setAccelerometerRange(MPU6050_RANGE_16_G); delay(100);
} void loop() {
// Get new Accelerometer sensor events with the readings sensors_event_t a, g, temp; mpu.getEvent(&a, &g, &temp); data.ForwardForce0 = mapAndAdjustPressureSensorValues(analogRead(A0), false); Serial.print(„Pressure Sensor0 LoopValue:“); Serial.println(analogRead(A0)); data.RightForce1 = mapAndAdjustPressureSensorValues(analogRead(A1), false); Serial.print(„Pressure Sensor3 LoopValue:“); Serial.println(analogRead(A1)); data.LeftForce2 = mapAndAdjustPressureSensorValues(analogRead(A2), false); Serial.print(„Pressure Sensor3 LoopValue:“); Serial.println(analogRead(A2)); data.UpDownForce3 = mapAndAdjustAccelerometerSensorValues(a.acceleration.y, false); Serial.print(„Pressure Sensor1 LoopValue:“); Serial.println(a.acceleration.y); esp_err_t result = esp_now_send(receiverMacAddress, (uint8_t *)&data, sizeof(data)); if (result == ESP_OK) { Serial.println(„Sent with success“); } else { Serial.println(„Error sending the data“); } delay(1000); }
Programmierung des Esp32 ( Sender)
Der Code für den Empfänger ist etwas komplizierter. Eine Schwierigkeit die es zu überwinden gab war es zu gewährleisten dass es keine Konflikte bei der Auslesung gleichtzeitig Empfanger Sensordaten gibt. Als Basis verwenden wir ein Xiao32-S2 in verbindung mit einem Lipo Akku und 3 Servomotren.
Datendefinition und -struktur: Es gibt eine Struktur namens PacketData, die Datenfelder für vier verschiedene Kräfte (Vorwärts, Rechts, Links, Hoch/Runter) enthält. Diese Struktur wird verwendet, um Daten vom Sender zu empfangen. Jede Kraft wird als Byte gespeichert.
Setup der Servomotoren: In der Funktion setUpServos() werden die Servomotoren initialisiert und ihre Positionen festgelegt.
ESP-NOW Callback: Die Funktion OnDataRecv ist eine Callback-Funktion, die aufgerufen wird, wenn Daten von einem anderen ESP32-Gerät empfangen werden. Die empfangenen Daten werden in die receiverData-Struktur kopiert. Danach werden die Funktionen mapAndWriteValues() und moveServosBasedOnValues() aufgerufen.
Werte zuordnen und schreiben: In der Funktion mapAndWriteValues() werden die empfangenen Daten analysiert und an die Funktionen zur Steuerung der Servomotoren weitergeleitet.
Servomotoren bewegen: In der Funktion moveServosBasedOnValues() werden die Servomotoren basierend auf den empfangenen Daten bewegt.
Setup-Funktion: In der setup()-Funktion wird die serielle Kommunikation initialisiert, die Servomotoren werden eingerichtet und ESP-NOW wird gestartet. Außerdem wird die Funktion OnDataRecv als Callback-Funktion für den Empfang von Daten registriert.
/* ESP-NOW GLOBALS *********************************************** **********/
#include <esp_now.h>
#include <WiFi.h>
// This is signal timeout in milli seconds. We will reset the data if no signal
#define SIGNAL_TIMEOUT 1000 unsigned long lastRecvTime = 0; typedef struct PacketData { byte ForwardForce0; byte RightForce1; byte LeftForce2; byte UpDownForce3; } PacketData;
PacketData receiverData;
/* SERVO GLOBALS ************************************************** *******/
#include „ESP32_S2_ISR_Servo.h“
#define TIMER_INTERRUPT_DEBUG 1
#define ISR_SERVO_DEBUG 1
// Select different ESP32 timer number (0-3) to avoid conflict
#define USE_ESP32_TIMER_NO 3
//See Adafruit QT Py ESP32-S2 : https://cdn-learn.adafruit.com/assets/ assets/000/107/493/large1024/adafruit_products_Adafruit_QT_Py_ESP32S2_Pinout.png?1640130293
#define PIN_D5 5 // TX = PIN_D5
#define PIN_D6 6 // SCL = PIN_D6
#define PIN_D7 7 // SDA = PIN_D7
#define PIN_D8 8 // A3 = PIN_D8
#define PIN_D9 9 // A2 = PIN_D9
#define PIN_D17 17 // A1 = PIN_D17
#define PIN_D18 18 // A0 = PIN_D18
// Published values for SG90 servos; adjust if needed
#define MIN_MICROS 800 //544
#define MAX_MICROS 2450 int servoA0 = -1; int servoA1 = -1; int servoA2 = -1; int servoA3 = -1; int valForwardForce0 = 0; int valRightForce1 = 0; int valLeftForce2 = 0; int valUpDownForce3 = 0; int maxSensorValue = valForwardForce0; // Assume sensor1 is the largest initially int maxSensorIndex = 1; // Variable to store the sensor number int UpDownNeutralIndex = 1; int position = 40; int backFinDeg = 80;
/* SETUP DEFAULT **************************************************
*********/ void setInputDefaultValues() { // The middle position for joystick. (254/2=127) receiverData.ForwardForce0 = valForwardForce0; //receiverData.UpForce2 = valUpForce1; }