6.9 KiB
Krmilnik v keglu
Vključene knjižnice, definicije
Nastavljive zadeve
Ker imamo več keglov, najprej povemo, za katerega gre. Za vsak kegel je treba cifro prilagoditi. Mogoče bi nekoč lahko koncipirali boljši sistem dodajanja keglov.
#define KEGEL_ID 1
Navesti moramo tudi MAC naslov sprejemnika. Mora se skladati s kodo sprejemnika!
uint8_t sprejemnikMac[] = {0x08, 0x3A, 0xF2, 0x50, 0xEF, 0x6C };
Interval pošiljanja odčitkov senzorjev (v milisekundah).
#define DISPLAY_INTERVAL 5 // interval between pose displays
i2c
Senzorske vrednosti beremo preko i2c protokola, za kar skrbi knjižnica Wire.h
.
#include <Wire.h>
Razbiranje orientacije
Sledeče knjižnice so potrebne za branje senzorja, vključene iz zdaj žal ne več aktivno razvite knjižnice RTIMULib-Arduino. Prenesemo zadnjo verzijo in vse direktorije znotraj libraries
skopiramo v ~/Arduino/libraries
. Nato odkomentiramo primerni senzor v ~/Arduino/libraries/RTIMULib/RTIMULibDefs.h
, torej #define MPU9250_68
. Vsi ostali naj ostanejo odkomentirani!
#include "I2Cdev.h"
#include "RTIMUSettings.h"
#include "RTIMU.h"
#include "RTFusionRTQF.h"
#include "CalLib.h"
Kalibracijski podatki
Kegel moramo pred uporabo kalibrirati, s to skico. S tipko s
shranimo parametre kalibracije v EEPROM
, od koder ga ta skica kasneje prebere.
#include <EEPROM.h>
Brezžično povezovanje
Potrebujemo pa tudi knjižnice za brezžično pošiljanje preko WiFi-ja, z ESP-NOW
komunikacijskim protokolom.
#include <esp_now.h>
#include <WiFi.h>
Podatkovne strukture
Paket senzorskih podatkov
Vsak brezžični paket pošlje ID kegla, razbran pospešek (z odšteto gravitacijo) po X, Y in Z oseh in pa kvaternion, ki opisuje orientacijo (X Y Z W).
Rezerviramo eno spremenljivko za odčitek senzorja.
typedef struct sensor_msg {
int id;
RTFLOAT aX;
RTFLOAT aY;
RTFLOAT aZ;
RTFLOAT qX;
RTFLOAT qY;
RTFLOAT qZ;
RTFLOAT qW;
} sensor_msg;
sensor_msg odcitek;
Objekti za gibalni senzor
RTIMU *imu; // Podatki o senzorju
RTFusionRTQF fusion; // fuzijski objekt
RTIMUSettings settings; // nastavitve senzorja
RTQuaternion gravity; // kvaternion za smer gravitacije
Števci za ritem pošiljanja paketov.
unsigned long lastDisplay;
unsigned long lastRate;
int sampleCount;
Podatki o brezžični povezavi
esp_now_peer_info_t peerInfo;
Vzpostavitev
Najprej rezerviramo prostor za razbiranje napake in vzpostavimo serijsko povezavo.
void setup() {
int errcode; // Prostor za napako
// Serijski port na navedeni hitrosti
Serial.begin(115200);
// Obvestimo o začenjanju
Serial.println("Starting up...");
Vklopimo tudi EEPROM razbiranje ter i2c povezavo s senzorjem.
// Init EEPROM based on magnet calibration size requirement
EEPROM.begin(512);
// I2C init
Wire.begin();
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
Nato ustvarimo objekt za gibalni senzor in obvestimo o morebitni napaki.
// create the imu object
imu = RTIMU::createIMU(&settings);
Serial.print("ArduinoIMU starting using device "); Serial.println(imu->IMUName());
if ((errcode = imu->IMUInit()) < 0) {
Serial.print("Failed to init IMU: "); Serial.println(errcode);
}
Razberemo kalibracijo magnetometra.
if (imu->getCalibrationValid())
Serial.println("Using compass calibration");
else
Serial.println("No valid compass calibration data");
In pa nastavimo izhodiščno (nevtralno) smer gravitacije.
gravity.setScalar(0);
gravity.setX(0);
gravity.setY(0);
gravity.setZ(1);
Števce nastavimo na izhodiščni čas.
lastDisplay = lastRate = millis();
sampleCount = 0;
WIFI nastavimo na ESP-NOW.
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
Nato nastavimo v začetku definiran MAC naslov sprejemnika, wifi kanal ter nastavimo nekriptiran način delovanja.
memcpy(peerInfo.peer_addr, sprejemnikMac, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("WIFI registracija ni uspela");
return;
}
}
Zanka delovanja
TODO opis zanke!
void loop() {
unsigned long now = millis();
unsigned long delta;
RTVector3 realAccel;
RTQuaternion rotatedGravity;
RTQuaternion fusedConjugate;
RTQuaternion qTemp;
int loopCount = 0;
// get the latest data if ready yet
while (imu->IMURead()) {
// this flushes remaining data in case we are falling behind
if (++loopCount >= 10)
continue;
fusion.newIMUData(imu->getGyro(), imu->getAccel(), imu->getCompass(), imu->getTimestamp());
// do gravity rotation and subtraction
// create the conjugate of the pose
fusedConjugate = fusion.getFusionQPose().conjugate();
// now do the rotation - takes two steps with qTemp as the intermediate variable
qTemp = gravity * fusion.getFusionQPose();
rotatedGravity = fusedConjugate * qTemp;
// now adjust the measured accel and change the signs to make sense
realAccel.setX(-(imu->getAccel().x() - rotatedGravity.x()));
realAccel.setY(-(imu->getAccel().y() - rotatedGravity.y()));
realAccel.setZ(-(imu->getAccel().z() - rotatedGravity.z()));
sampleCount++;
if ((delta = now - lastRate) >= 1000) {
//Serial.print("Sample rate: "); Serial.print(sampleCount);
if (!imu->IMUGyroBiasValid()) {
// Serial.println(", calculating gyro bias");
} else {
// Serial.println();
}
sampleCount = 0;
lastRate = now;
}
if ((now - lastDisplay) >= DISPLAY_INTERVAL) {
lastDisplay = now;
odcitek.id = KEGEL_ID;
odcitek.aX = realAccel.x();
odcitek.aY = realAccel.y();
odcitek.aZ = realAccel.z();
odcitek.qX = fusion.getFusionQPose().x();
odcitek.qY = fusion.getFusionQPose().y();
odcitek.qZ = fusion.getFusionQPose().z();
odcitek.qW = fusion.getFusionQPose().scalar();
esp_err_t result = esp_now_send(sprejemnikMac, (uint8_t *) &odcitek, sizeof(odcitek));
if (result == ESP_OK) {
Serial.println("Uspesno poslano");
} else {
Serial.println("Napaka pri posiljanju");
}
}
}
}