From 52cc7bfa1adf19623345dc48387cb673f1424aad Mon Sep 17 00:00:00 2001 From: Fisch Date: Sun, 10 Apr 2022 18:36:19 +0200 Subject: [PATCH] oled and nrf with teensy working --- .../length_calibration_20220410.ods | Bin 0 -> 30314 bytes hoverbrettctrl/platformio.ini | 6 +- hoverbrettctrl/src/main.cpp | 256 ++++++++++++++---- 3 files changed, 209 insertions(+), 53 deletions(-) create mode 100644 hoverbrettctrl/length_calibration_20220410.ods diff --git a/hoverbrettctrl/length_calibration_20220410.ods b/hoverbrettctrl/length_calibration_20220410.ods new file mode 100644 index 0000000000000000000000000000000000000000..52e7a5237b4438ce044d8c545fed2344dd499e52 GIT binary patch literal 30314 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 #define SERIAL_BAUD 115200 // [-] Baud rate for built-in Serial (used for the Serial Monitor) +#include +#include +#include +#include +//128 x 64 px +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define SCREEN_ADDRESS 0x3C +#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +#define DISPLAYUPDATE_INTERVAL 200 + uint8_t error = 0; #define IMU_NO_CHANGE 2 //IMU values did not change for too long @@ -24,12 +36,45 @@ long last_adcupdated=0; #define CONTROLUPDATEPERIOD 10 long last_controlupdate = 0; -#define GT_LENGTH_OFFSET 4090 //adc offset value (rolled up value) -#define GT_LENGTH_MIN 220 //length in mm at which adc values start to change -#define GT_LENGTH_SCALE -0.73 //(offset-adcvalue)*scale = length[mm] (+length_min) -//2720 at 1000mm+220mm -> 1370 for 1000mm -> -#define GT_LENGTH_MAXLENGTH 2500 //maximum length in [mm]. maximum string length is around 2m80 +#define GT_LENGTH_MIN 200 //minimum length for stuff to start happen + +#define GT_LENGTH_1_OFFSET -22.5 +#define GT_LENGTH_1_SCALE 2.5 +#define GT_LENGTH_2_OFFSET 563.6 +#define GT_LENGTH_2_SCALE 0.45 + +#define GT_LENGTH_CROSSOVERADC ((GT_LENGTH_2_OFFSET-GT_LENGTH_1_OFFSET)/(GT_LENGTH_1_SCALE-GT_LENGTH_2_SCALE)) //crossover point from adc, where first and second lines cross +#define GT_LENGTH_CROSSOVER_FEATHER 76.0 //how much adc change in both directions should be smoothed when switching between first and second line + +#define GT_LENGTH_MAXLENGTH 2000 //maximum length in [mm]. maximum string length is around 2m80 +#define GT_LENGTH_ADC_MAXDIFF 127 //maximum adc value difference between A and B poti. Used to detect scratching poti. during length calibration was 57 +int raw_length_maxdiff=0; +//TODO: implement error for poti maxdiff uint16_t gt_length=0; //0=rolled up, 1unit = 1mm +/* calibration 20220410 +lenght[mm], adc +0,9 +100,52 +200,86 +300,124 +400,165 +500,212 +600,286 +700,376 +800,520 +900,746 +1000,984 +1100,1198 +1200,1404 +1300,1628 +1400,1853 +1500,2107 +1600,2316 +1700,2538 +1800,2730 +1900,2942 +2000,3150 +*/ #define GT_VERTICAL_CENTER 2048 //adc value for center position #define GT_VERTICAL_RANGE 2047 //adc value difference from center to maximum (30 deg) @@ -73,7 +118,8 @@ struct nrfdata { nrfdata lastnrfdata; long last_nrfreceive = 0; //last time values were received and checksum ok -long nrf_delay = 0; +unsigned long nrf_delay = 0; +unsigned long last_nrfreceive_delay=0; #define MAX_NRFDELAY 100 //ms. maximum time delay at which vehicle will disarm boolean radiosendOk=false; @@ -104,36 +150,65 @@ uint8_t controlmode=0; #define MODE_GAMETRAK 2 +void updateDisplay(unsigned long loopmillis); + + void setup() { Serial.begin(SERIAL_BAUD); //Debug and Program esc.init(); + analogReadResolution(12); - pinMode(PIN_GAMETRAK_LENGTH_A, INPUT); - pinMode(PIN_GAMETRAK_LENGTH_B, INPUT); - pinMode(PIN_GAMETRAK_VERTICAL, INPUT); - pinMode(PIN_GAMETRAK_HORIZONTAL, INPUT); + pinMode(PIN_GAMETRAK_LENGTH_A, INPUT_PULLUP); + pinMode(PIN_GAMETRAK_LENGTH_B, INPUT_PULLUP); + pinMode(PIN_GAMETRAK_VERTICAL, INPUT_PULLUP); + pinMode(PIN_GAMETRAK_HORIZONTAL, INPUT_PULLUP); + + Wire.begin(); + // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally + if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { + Serial.println(F("SSD1306 allocation failed")); + for(;;); // Don't proceed, loop forever + } + + // Show initial display buffer contents on the screen -- + // the library initializes this with an Adafruit splash screen. + display.display(); + + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(SSD1306_WHITE); + display.setCursor(10, 0); + display.println(F("Radio Init")); + display.display(); // Show initial text radio.begin(); - //Serial1.println("set rate"); + Serial.println("RF24 set rate"); radio.setDataRate( RF24_250KBPS ); //set to slow data rate. default was 1MBPS //radio.setDataRate( RF24_1MBPS ); - //Serial1.println("set channel"); + //Serial.println("set channel"); radio.setChannel(NRF24CHANNEL); //0 to 124 (inclusive) - //Serial1.println("set retries and payload"); + //Serial.println("set retries and payload"); radio.setRetries(15, 15); // optionally, increase the delay between retries & # of retries radio.setPayloadSize(8); // optionally, reduce the payload size. seems to improve reliability - //Serial1.println("open pipe"); + //Serial.println("open pipe"); radio.openWritingPipe(pipes[0]); //write on pipe 0 radio.openReadingPipe(1, pipes[1]); //read on pipe 1 - //Serial1.println("start listening"); + Serial.println("start listening"); radio.startListening(); + + display.clearDisplay(); + display.setTextSize(2); // Draw 2X-scale text + display.setTextColor(SSD1306_WHITE); + display.setCursor(10, 0); + display.println(F("Started")); + display.display(); // Show initial text } @@ -145,41 +220,82 @@ void loop() { - if (millis() - last_adcupdated > ADC_UPDATEPERIOD) { //update analog readings + if (loopmillis - last_adcupdated > ADC_UPDATEPERIOD) { //update analog readings - gt_length = constrain(( analogRead(PIN_GAMETRAK_LENGTH_A))*GT_LENGTH_SCALE - (GT_LENGTH_SCALE*GT_LENGTH_OFFSET) +GT_LENGTH_MIN, 0,GT_LENGTH_MAXLENGTH); + int raw_length_a=analogRead(PIN_GAMETRAK_LENGTH_A); + int raw_length_b=analogRead(PIN_GAMETRAK_LENGTH_B); + + raw_length_maxdiff=max(raw_length_maxdiff,abs(raw_length_a-raw_length_b)); + + int raw_length=(raw_length_a+raw_length_b)/2; + + + uint16_t gt_length_1 = GT_LENGTH_1_OFFSET+raw_length*GT_LENGTH_1_SCALE; + uint16_t gt_length_2 = GT_LENGTH_2_OFFSET+raw_length*GT_LENGTH_2_SCALE; + + double crossovermapping=constrain(((raw_length-GT_LENGTH_CROSSOVERADC)/GT_LENGTH_CROSSOVER_FEATHER )/2.0+0.5, 0.0,1.0); //0 for first, 1 for second + + + gt_length = constrain( gt_length_1*(1-crossovermapping) + gt_length_2*crossovermapping , 0,GT_LENGTH_MAXLENGTH); if (gt_length<=GT_LENGTH_MIN){ gt_length=0; //if below minimum measurable length set to 0mm } - gt_vertical = constrain(map(analogRead(PIN_GAMETRAK_VERTICAL)-((int16_t)GT_VERTICAL_CENTER), +GT_VERTICAL_RANGE,-GT_VERTICAL_RANGE,-127,127),-127,127); //left negative - gt_horizontal = constrain(map(analogRead(PIN_GAMETRAK_HORIZONTAL)-((int16_t)GT_HORIZONTAL_CENTER), +GT_HORIZONTAL_RANGE,-GT_HORIZONTAL_RANGE,-127,127),-127,127); //down negative + gt_vertical = constrain(map(analogRead(PIN_GAMETRAK_VERTICAL)-((int16_t)GT_VERTICAL_CENTER), -GT_VERTICAL_RANGE,+GT_VERTICAL_RANGE,-127,127),-127,127); //left negative + gt_horizontal = constrain(map(analogRead(PIN_GAMETRAK_HORIZONTAL)-((int16_t)GT_HORIZONTAL_CENTER), -GT_HORIZONTAL_RANGE,+GT_HORIZONTAL_RANGE,-127,127),-127,127); //down negative last_adcupdated = millis(); /* - Serial1.print("gt_length="); - Serial1.print(gt_length); - Serial1.print(", gt_vertical="); - Serial1.print(gt_vertical); - Serial1.print(", gt_horizontal="); - Serial1.println(gt_horizontal);*/ + Serial.print("gt_length="); + Serial.print(gt_length); + Serial.print(", gt_vertical="); + Serial.print(gt_vertical); + Serial.print(", gt_horizontal="); + Serial.print(gt_horizontal); + - /* - Serial1.print("PIN_GAMETRAK_LENGTH="); - Serial1.print(analogRead(PIN_GAMETRAK_LENGTH)); - Serial1.print(", PIN_GAMETRAK_VERTICAL="); - Serial1.print(analogRead(PIN_GAMETRAK_VERTICAL)); - Serial1.print(", PIN_GAMETRAK_HORIZONTAL="); - Serial1.println(analogRead(PIN_GAMETRAK_HORIZONTAL));*/ + + Serial.print(" pl="); + Serial.print(raw_length_a); + Serial.print(", "); + Serial.print(raw_length_b); + + Serial.print(", pv="); + Serial.print(analogRead(PIN_GAMETRAK_VERTICAL)); + Serial.print(", ph="); + Serial.print(analogRead(PIN_GAMETRAK_HORIZONTAL)); + Serial.print(" Ldiff="); + Serial.println(abs(raw_length_a-raw_length_b)); + */ + + + /* + static int _rawlengtharray[40]; + static int _rawlapos=0; + _rawlengtharray[_rawlapos++]=raw_length; + _rawlapos%=40; + + int rawlengthfilter=0; + for (int p=0;p<40;p++) { + rawlengthfilter+=_rawlengtharray[p]; + } + rawlengthfilter/=40; + + static int maxldiff=0; + maxldiff=max(maxldiff,abs(raw_length_a-raw_length_b)); + Serial.print(""); + Serial.print(rawlengthfilter); + Serial.print(" maxldiff="); + Serial.println(maxldiff);*/ } //NRF24 - nrf_delay = millis() - last_nrfreceive; //update nrf delay + nrf_delay = loopmillis - last_nrfreceive; //update nrf delay if ( radio.available() ) { - //Serial1.println("radio available ..."); + //Serial.println("radio available ..."); lastpacketOK = false; //initialize with false, if checksum ok gets set to true //digitalWrite(PIN_LED, !digitalRead(PIN_LED)); @@ -195,7 +311,9 @@ void loop() { uint8_t calcchecksum = (uint8_t)((lastnrfdata.steer + 3) * (lastnrfdata.speed + 13)); if (lastnrfdata.checksum == calcchecksum) { //checksum ok? lastpacketOK = true; - last_nrfreceive = millis(); + last_nrfreceive_delay=loopmillis-last_nrfreceive; //for display purpose + last_nrfreceive = loopmillis; + //parse commands motorenabled = (lastnrfdata.commands & (1 << 0))>>0; //check bit 0 @@ -207,15 +325,15 @@ void loop() { if (controlmode == MODE_RADIONRF && nrf_delay >= MAX_NRFDELAY) { //too long since last sucessful nrf receive controlmode = MODE_DISARMED; #ifdef DEBUG - Serial1.println("nrf_delay>=MAX_NRFDELAY, disarmed!"); + Serial.println("nrf_delay>=MAX_NRFDELAY, disarmed!"); #endif } if (controlmode == MODE_RADIONRF) { //is armed in nrf mode if (lastpacketOK) { //if lastnrfdata is valid - if (millis() - last_controlupdate > CONTROLUPDATEPERIOD) { - last_controlupdate = millis(); + if (loopmillis - last_controlupdate > CONTROLUPDATEPERIOD) { + last_controlupdate = loopmillis; //out_speed=(int16_t)( (lastnrfdata.y-TRACKPOINT_CENTER)*1000/TRACKPOINT_MAX ); //out_steer=(int16_t)( -(lastnrfdata.x-TRACKPOINT_CENTER)*1000/TRACKPOINT_MAX ); @@ -237,7 +355,7 @@ void loop() { #ifdef DEBUG if (!lastpacketOK) { - Serial1.println("Armed but packet not ok"); + Serial.println("Armed but packet not ok"); } #endif @@ -247,18 +365,18 @@ void loop() { if (controlmode==MODE_DISARMED) { //check if gametrak can be armed if (gt_length>gt_length_set && gt_length-GT_LENGTH_MINDIFF & _gt_length_diff-GT_LENGTH_MINDIFF) && (_gt_length_diff