Arduino farbige Fonts (ESP32)

1. Für jeden Buchstaben

1.1 Einzelnen Buchstaben markieren

1.2  Markierung Kopieren (Strg+C)

1.3 Einfügen als Neues Bild (Strg+Shift+V)

1.4 Bild auf Inhalt zuschneiden(Shortcut in den Settings vergeben)

2.5 Speichern als „A.xcf“ (Strg+S)

3. Installieren des Davids Batch Processor und erster Schritt

3.1 Input: Add all XCF Files

3.2 Output as BMP

3.3 Bilderordner öffnen und in der ansicht Breite und Höhe Spalten anzeigen lassen

3.4 Bilder nach Breite und Höhe sortieren und höchste wie breiteste Pixel aufschreiben.

3.5 Gegebenfalls einzelne sehr breite /Hohe Buchstaben nacheditieren (Zum Beispiel W)

4.1 Add BMPs from Folder in Batch Processor

4.2 Gehe zu Resize, Enable, absolute Centered with Padding Background Black

4.3 Resize zum höchsten und breitesten Pixeln

5. IrfanView Batch Renaming

5.1 Bilder zur Stapelverarbeitung hinzufügen  und nach P# umbenennen

6. Konvertiere Bilder zu Image.bin und array.h Datei

6.1 Dazu habe ich ein kleines Javatool entwickelt das PNummer.bmp durchnummerierte Bilder im gleichen Ordner als Image.bin / test.h byte array konvertiert.

Wahlweise RGB8,RGB565, RGB24

Zum testen benutze ich die langsame Methode über das Imagefile im SPIFF, für den schnellen Bilderaufbau grösser 25fps benutze ich das Byte Array .. ein Bild mit 480x320px*3Bytes benötigt 80ms via 40Mhz SPI. Bei Bildern mit zum Beispiel 100×90 Pixeln werden im update 100FPS erreicht.

Downloadlink TFT-Tool

TFT_ESPI Example Script ILI9488:

#include <Wire.h>
#include "RTClib.h"
#include "EEPROM.h"
#define EEPROM_SIZE 64
#include <TimeLib.h>
#include "FS.h"
#include "SPIFFS.h"
#include <TFT_eSPI.h>
#include <SPI.h>
//#include "test.h"
#include "esp_spi_flash.h"

TFT_eSPI tft = TFT_eSPI();

#define SPI2_NSS_PIN 28
static const int spiClk = 40000000; // 1 MHz
SPIClass * vspi = NULL;


unsigned long bgstart = 675000UL;
void setup() {
  Serial.begin(115200);
  if (!SPIFFS.begin(true)) {
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }
  btStop();


  vspi = new SPIClass(VSPI);
  vspi->begin();

  Serial.begin(115200);
  tft.begin();
  tft.setRotation(1);  // landscape

  tft.fillScreen(TFT_BLACK);
  //Background
  bigImage(702000, 702000, 0, 0, 480, 320, 0xFFFFFF);
  //void DrawString( uint32_t check, int x, int y, int width, int height, String text, bool compare, bool transp,int asciioffset)
  DrawString(  0  , 0, 0, 90, 100, "HELLO", false, false, 65);
  DrawString(  0  , 0, 100, 90, 100, "ITS", false, false, 65);
  DrawString(  0  , 0, 200, 90, 100, "FUN", false, false, 65);
}
void loop() {

}

void DrawString( uint32_t check, int x, int y, int width, int height, String text, bool compare, bool transp, uint32_t asciioffset) {
  int lengthl = text.length();
  for (int l = 0; l < lengthl; l++)
  {
    uint32_t charpos = ((uint32_t)((uint32_t)text.charAt(l) - (uint32_t)asciioffset) * (uint32_t)width * height * 3);

    //void transparentImage(uint32_t startpic, uint32_t startbackground, int x, int y, int width, int height, uint32_t tcolor)
    transparentImage( check + charpos, 702000, x + ( l * width), y, width, height, 0xFFFFFF);
    Serial.println(charpos);

  }
}

void transparentImage(uint32_t startpic, uint32_t startbackground, int x, int y, int width, int height, uint32_t tcolor) {
  tft.setWindow(x, y, x + width - 1, y + height - 1);
  //read background clipping
  File f = SPIFFS.open( "/test.bin", "r");
  char *buf = (char*)malloc(width * height * 3);
  char *temp = (char*)malloc(width * 3);
  unsigned long offset = 0;
  for (int i = 0; i < height; i++) {
    // unsigned long offset = startbackground + (x * width*3 - (width*3 - (width-y*3)) + (i * width * 3));
    unsigned long offsetbg = startbackground + ((y + i) * 480 * 3 ) + (x * 3) ;
    f.seek(offsetbg, SeekSet);
    f.readBytes((char*)temp, width * 3) ;
    memcpy( buf + (i * width * 3), temp, (width * 3) );

  }

  for (int i = 0; i < height; i++) {
    unsigned long offset = startpic + i * width * 3;
    f.seek(offset, SeekSet);
    f.readBytes((char*)temp, width * 3) ;
    for (int h = 0; h < width * 3; h = h + 3)
    {
      uint32_t bp = temp[ h];
      bp = (bp << 8) | temp[h + 1];
      bp = (bp << 16) | temp[ h + 2];
      if (bp >= 0x111111 || y + i > 300) {
        buf[width * i * 3 + h] = temp[h];
        buf[width * i * 3 + h + 1] = temp[h + 1];
        buf[width * i * 3 + h + 2] = temp[h + 2];
      }
    }
  }

  vspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
  digitalWrite(5, LOW); //pull SS slow to prep other end for transfer
  vspi->writeBytes((uint8_t*)buf, width * height  * 3);
  digitalWrite(5, HIGH); //pull ss high to signify end of data transfer
  vspi->endTransaction();
  f.close();
  Serial.println("test");
  free(buf);
  free(temp);
}

void bigImage(uint32_t startpic, uint32_t startbackground, int x, int y, int width, int height, uint32_t tcolor) {
  tft.setWindow(x, y, x + width - 1, y + height - 1);
  //read background clipping
  File f = SPIFFS.open( "/test.bin", "r");
  char *buf = (char*)malloc(width * height * 3);
  char *temp = (char*)malloc(width * 3);
  unsigned long offset = 0;
  vspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
  digitalWrite(5, LOW); //pull SS slow to prep other end for transfer
  for (int i = 0; i < height; i++) {
    unsigned long offset = startbackground + (i * width * 3);
    f.seek(offset, SeekSet);
    f.readBytes((char*)temp, width * 3) ;

    vspi->writeBytes((uint8_t*)temp, width * 3);
  }
  digitalWrite(5, HIGH); //pull ss high to signify end of data transfer
  vspi->endTransaction();
  f.close();

  free(buf);
  free(temp);
}

Step5 Standard Dokumentation zu HTML Parser

Neue Version V1.0.8 .. ADD: Fast alle Daten-Operanden hinzugefügt DD,DW,MW etc..

Download:S5ParserV1-0-8

 

Achtung, es wird nur Java ab Version 7 mit aktuellen Updates unterstützt, auch nicht mehr offiziell unterstützte Betriebssysteme wie WinXP werden Umwandlungsfehler verursachen.

Der Step5 Doku Parser ermöglicht es die reine Standard Text Dokumentation (nicht komfortausgabe) aus Step5 in ein Html-Dokument umzuwandeln um dieses auf dem Smartphone wie auch auf jedem PC im Browser anzuzeigen.

Optional kann man die Zuordnungsdatei angeben das diese als Split-Bildschirm angezeigt wird.

Alle Netzwerke und Operanden werden in eine Temporäre Speicher-Datenbank eingelesen.. daraus werden dann für alle Operanden Verweise auf das nächste Netzwerk in dem diese vorkommen erstellt. Wenn man Die FB-Dokumentation an die PB Dokumentation anhängt, werden vorkommen von Operanden auch dort weiterverlinkt und in den Mouse-Over Listen angezeigt.

Es werden als Tooltip ebenso Querverweislisten bei MouseOver über den Operanden angezeigt.

Die Nutzung der Software erfolgt ohne Gewähr und ohne Zusicherung einer bestimmten Funktion.

Changelog

Neue Version V1.0.7 .. FIX: MeRker mit mehreren Nullen wurden mit 2 Punkten verlinkt

Neue Version V1.0.6 .. FIX: Es werden jetzt alle Operanden gefunden.

Neue Version V1.0.5 .. Diverse kleinere Bugs die im Testdokument nicht auffielen behoben!  z.B. Fehlende Verweise, doppelte Netzwerke, fehlende Netzwerke.

Neue Version V1.0.4 .. Unterstützung von NEUEN Bausteinen OB,FB,FX,SB (Diverse GRÖßere Bugs die im Testdokument nicht auffielen behoben)

Neue Version V1.0.3 .. Unterstützung von Funktionsbausteinen (Können für Verlinkungen an die PB Datei angehängt werden) Diverse kleinE Bugs behoben.

Version V1.0.2: Besseres LAyout FÜR verbesserte Bedienbarkeit 

DOWNLOAD: S5ParserV1-0-8

 

 

Arduino Taster Entprellung

Entprellung eines Tasters mit den Zeitoptionen Mindeste Druckzeit, Entprellzeit, Abtastrate (indirekte Entprellung).

int pin = 6;
int laststate = HIGH;
int state = HIGH;
boolean debouncing = false; //Entprellt?
boolean minimpuls = true; //Minimale Impulslänge für Aktion
unsigned long lastintervallmillis = millis();
unsigned long changestate = millis();
unsigned long lastdebounce = millis();
unsigned long lastminpulse = millis();

void setup() {
  Serial.begin(115200);
}

//Konfiguration:
int Abtastrate = 5;
int entprellzeit = 100;
int minpuls = 50; //Minimale Pulslänge


void loop() {
  long tempmillis = millis();
  if ((tempmillis - lastintervallmillis) >=  Abtastrate ) //Frühestens nach Abtastrate überprüfen, reicht manchmal schon als kleine Entprellung
  {
    lastintervallmillis = tempmillis; //Letzte Abtastung speichern
    int tempstate = digitalRead(pin); //Pin einlesen

    if (tempstate != laststate) //Pin hat sich geändert, hier ist egal in welche Richtung, im Prinzip Low und High Entprellung.
    {
      debouncing = true; //Entprellung aktivieren
      minimpuls=true;
      laststate = tempstate; //Buffer für temporären Pinstatus
      changestate = tempmillis; //Änderungszeitpunkt
    }
    if (debouncing) { 
      if ((tempmillis - changestate) >= entprellzeit && debouncing)
      {
        debouncing = false;
      }
    }
    if (tempmillis - changestate >= (entprellzeit + minpuls) && !debouncing )
    {
      minimpuls=false;
    }
    if(!debouncing && !minimpuls)
      state = laststate;
      debouncing = true;
      minimpuls = true;
      if (state == HIGH) {
        Serial.println("Alles erfüllt!");
      }
    }
  }
}

 

MCP23017 Deustch

MCP 23017 Kurz und Bündig.
Allgemeine Register.

IODIR
Direction Register, bestimmt ob ein Pin Input oder Output ist.
1=Input, 0=Output

IPOL
Polaritätsregister, bestimmt die Polarität/Invertierung eines Pins
1=Invertiert, 0=nicht Invertiert

GPIO
InputOutput Register,
LESEN: Eingänge einlesen.
SCHREIBEN: wird ans OLAT Output Register weitergeleitet.
0=LOW, 1=HIGH

OLAT
Output Latch Register,
SCHREIBEN: Setzt die Ausgänge der als Ausgang definierten Pins.
LESEN: Gibt den zuletzt gesetzten Wert zurück.
0=LOW, 1=HIGH

GPPU
Pullup Register, aktiviert den Pullup am Pin.
1=HIGH, 0=LOW

Interrupt Register Konfiguration

Kleine Falle:
Die Interruptverwendung beinhaltet 3 Register. GPINTVEN, DEFVAL, INTCON welche alle 3 gesetzt werden müssen.
Wenn der Interrupt für einen Pin mit GPINTVEN aktiviert wird, entscheidet INTCON die Datenquelle des Signalvergleichs.
Wenn INTCON 0 ist, wird der Pin mit dem in DEFVAL gesetzten Signal 0/1 verglichen,
sollte INTCON 1 sein wird auf den letzten Pin Status verglichen.

INTERRUPT LOGIK:

Wenn ein Interrupt generiert wird werden die aktuellen Pin Werte in das INTCAP Register gesichert. Der Interrupt wird gelöscht wenn das LSB Byte beim Lesen der Register GPIO oder INTCAP übertragen wurde.

GPINTEN
InterruptOnChange Event Register, Aktiviert den Interrupt für einen Pin.
1=Aktiv, 0=Inaktiv

INTCON
Interrupt Control Register, bestimmt die Quelle der Interrupt On Change Quelle
1= Vergleich auf das DEFVAL Register,0= Letzten Status des Pins

DEFVAL
Default Value Register, bestimmt den Standard Vergleichswert des Pins wenn INTCON=1
1= HIGH, 0=LOW

Interrupt Register Status/Information

INTF
Interrupt Flag Register, beinhaltet den Status der Interruptauslösung.
1=Interrupt ausgelöst, 0=Kein Interrupt vorhanden

INTCAP
Interrupt Capt Register, Speichert den Wert des letzten Pin Status bei Interrupt Auslösung.
1=HIGH, 0=LOW

Allgemeine Konfiguration

IOCON
Zwischen Port A und B geteiltes Konfigurationsregister

Bit7: BANK, entscheidet die Sortierung der IO Register,
1=Zuerst alle A Register, danach alle B register
0= Beispiel, GPIOA,GPIOB,IPOLA,IPOLB

Bit6: MIRROR,
1=A und B teilen sich beide Interrupt Pins INTA und INTB
0=INTA ist für A und INTB für B

Bit5: SEQOP, Sequentielles Lesen/Schreiben
1= Deaktiviert
0=Aktiviert

Bit4: DISSLW, i2C Slew Rate Clock Stretching Ein/Ausschalten
1=Deaktiviert
0=Aktiviert

Bit3: HAEN: Address Pins aktivieren/Deaktivieren
1=Aktiviert0=Deaktiviert

Bit2: ODR Konfiguration INTA, INTB Open-Drain Option
1=Open-Drain
0=Aktiver Treiber

Bit1: INTPOL Interrupt PIN Polarität, Invertiert die Interrupt Pins.

Bit0: Nicht benutzt.

Einfache Genaue Messungen mit Mikrocontrollern (Teil1)

Referenz-Signal

Als erstes benötigen wir ein Referenz-Signal, wozu brauchen wir das überhaupt?

Die Mikrocontroller werden üblicherweise von Quarzen getaktet, hier in den Beispielen nehmen wir ein 16Mhz Standard an. Normalerweise benötigt man die normalen Quarze, diese reichen für normale Taktungen und Kurzzeitmessungen im Nano/Mikrosekunden Bereich auch aus. Das eigentliche Problem tritt erst auf wenn man Sekunden oder sogar Stunden auf eine Millisekunde genau messen möchte, hier addieren sich die Messfehler dann auf.

Aber wieviel ist das?

Dazu benutze ich ein GPS-Empfänger für knapp 8€, dieser bekommt durchgehend von allen Empfangenen Satelliten ein Mikrosekunden Zeitstempel in Weltzeit mit der genauigkeit einer Atomuhr (Die wirklich in jedem Satellit vorhanden ist).

Durch eine eigene hohe Taktung kann das Empfängermodul aus den Empfangenen Daten der Satelliten die Lokalzeit in Millisekunden fast Nanosekundengenau berechnen. Wir machen uns zum Vorteil das jedes Modul ein PPS „Pulse Per Second“ Impuls an einem Pin ausgibt! Bei einem NEO-6M Modul ist dieser Impuls auf 60Nanosekunden genau Langzeitsynchronisiert, das bedeutet das solange wir diesen Puls Empfangen und zählen .. der Abstand der gezählten Impulse den Sekunden entsprechen und das auf 60ns genau.

Praktisch heißt das, wir können mit einem Mikrocontroller als Referenz andere Impulse zwischen zwei PPS Impulsen zählen und können so auf 30-60ns Genau Frequenzen im Mhz Bereich auf 1Hz genau messen.

 

Beispiel:

Man kauft sich ein fertiges Breadboard mit angegebenem 16MHz Resonator/Quarz.. Dank der Messung mit dem PPS Impuls stellt man dann fest das dieser im Schnitt nur 15,95MHz und das auch noch schwankend taktet. Das bedeutet immerhin das für den Mikrocontroller 1 Sekunde nur 997 Millisekunden lang ist und dieser nach 20 Minuten und 1200 Sekunden meint es wäre fast 4 Sekunden später usw usw!

Eine schlechte Stopuhr gegen eine Digitale Armbanduhr mit genauen Sekunden.. dabei hat man sich das ja so gut ausgedacht..

 

 

 

 

 

RPi Starwars Uhr mit Internetradio

43176914545393888441054441454726041099

Eine kleine RPi-Zero (5€ Minicomputer) Uhr die die aktuelle Uhrzeit in der Mitte anzeigt und bei Wechsel wie im StarWars Vorspann raus und wieder einfliesst.

Wenn man Internetzeit als Sync benutzen möchte kann man auch gleich ein Internetradio daraus bauen, ansonsten bieten sich Diverse RealTimeClocks an.

Das ganze basiert auf einem Headless-Linux (ohne HDMI Bildschirm und Grafik) Debian Wheezy mit installiertem Java8 und einer JavaFX Animation. Das Elektronik SPI-Interface Display ist über die IOs mit dem FBTFT Framebuffer Treiber eingebunden.

Als Internetradio App habe ich MPD ohne Grafikoberfläche gewählt, dafür gibt es viele Apps in den Android und IPhone Stores.

  • Basiert auf Rapbian Wheezy ohne DesktopManager, deinstalliertem LightDM, Java8FX Engine schreibt JavaFX direkt in den Framebuffer.

    Gute Anleitung als Grundlage, im Endeffekt einfach zu lang http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/RaspberryPiFX/raspberryfx.html

    EDIT /boot/config.txt ähnlich:

    # uncomment to force a specific HDMI mode (this will force VGA)
    hdmi_group=2
    hdmi_mode=87
    hdmi_cvt=320 240 60 1 0 0 0
    # uncomment to force a HDMI mode rather than DVI. This can make audio work in
    # DMT (computer monitor) modes
    #hdmi_drive=2
    
    # uncomment to increase signal to HDMI, if you have interference, blanking, or
    # no display
    #config_hdmi_boost=4
    
    # uncomment for composite PAL
    #sdtv_mode=2
    
    
    # Uncomment some or all of these to enable the optional hardware interfaces
    #dtparam=i2c_arm=on
    #dtparam=i2s=on
    dtparam=spi=on

     

  • 2

    Installiere fbcp um den Main Framebuffer zum LCD-Framebuffer zu kopieren.

         sudo apt-get install cmake git
         git clone https://www.github.com/tasanakorn/rpi-fbcp
         cd rpi-fbcp/
         mkdir build
         cd build
         cmake ..
         make
         install fbcp /usr/local/bin/fbcp
  • 3

    Erstelle start.sh zum Beispiel:

    #!/bin/sh
    #On Staging kernel not needed
    #sudo modprobe fbtft dma
    
    #Example for hy28B
    sudo modprobe fbtft_device name=hy28b fbtft_device.speed=3000000 rotate=90 fbtft_device.fps=20
    sleep 10
    fbcp&
    JAVAFX_DEBUG=1 java -Dcom.sun.javafx.experimental.embedded.3d=true -Dprism.glDepthSize=16 -jar /home/pi/StarWars.jar
    

     

    Update Kernel mit rpi-update:

    rpi-update
    reboot
    REPO_URI=https://github.com/notro/rpi-firmware rpi-update
    reboot
  • 5

    Downloade Java8 JDKu71 für ARM und extrahiere die Dateien. Exportiere gegebenfalls Deinen Javapath  EXPORT PATH zu opt/jdkXXX/bin

    sudo tar zxvf jdk-8u71-linux-arm32-vfp-hflt.tar.gz -C /opt/
    mv /opt/jdk1.8.0_71/ /opt/jdk1.8.0
  • 6 Kompiliere das StarWars JavaFX Script und kopiere das Hintergrundbild StarWars.jpg in den gleichen Ordner /Projektpfad :
    import java.io.File;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javafx.animation.Animation;
    import javafx.animation.Animation.Status;
    import javafx.animation.Interpolator;
    import javafx.animation.KeyFrame;
    import javafx.animation.KeyValue;
    import javafx.animation.PauseTransition;
    import javafx.animation.SequentialTransition;
    import javafx.animation.Timeline;
    import javafx.animation.TranslateTransition;
    import javafx.application.Application;
    import static javafx.application.Application.launch;
    import javafx.concurrent.Service;
    import javafx.concurrent.Task;
    import javafx.concurrent.WorkerStateEvent;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.geometry.Point3D;
    import javafx.scene.Group;
    import javafx.scene.PerspectiveCamera;
    import javafx.scene.Scene;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.paint.Color;
    import javafx.scene.text.Font;
    import javafx.scene.text.FontPosture;
    import javafx.scene.text.Text;
    import javafx.scene.text.TextAlignment;
    import javafx.stage.Stage;
    import javafx.stage.StageStyle;
    import javafx.util.Duration;
    
    public class TextCrawler extends Application {
    
        private static final String crawl = "22:24";
    
        @Override
        @SuppressWarnings("empty-statement")
        public void start(Stage stage) throws Exception {
            
            Group root = new Group();
            root.setTranslateX(160);
            root.setTranslateY(120);
          
    ImageView background = new ImageView(new Image("StarWars.jpg", 320, 240, false, true));
    background.relocate(-160, -120);
    root.getChildren().add(background);
    File file = new File("starfield.css"); 
      root.getStylesheets().add(file.toURI().toURL().toExternalForm());  
    
             //   root.setStyle("-fx-background-image: url('StarWars.jpg')");  
            Scene scene = new Scene(root);
            scene.setCamera(new PerspectiveCamera());
            //scene.setFill(Color.BLACK);
         
            stage.setScene(scene);
            stage.setWidth(320);
            stage.setHeight(240);
            stage.initStyle(StageStyle.UNDECORATED);
     
          
            Text text = new Text(getCurrentTimeStamp());
            text.setTextAlignment(TextAlignment.CENTER);
            text.setFill(Color.YELLOW);
            text.setFont(Font.font("Arial", FontPosture.REGULAR, 100));
            text.setRotationAxis(new Point3D(1.0, 0.0, 0.0));
            text.setRotate(-50.0);
            text.setTranslateX(-text.getLayoutBounds().getWidth() / 2);
            root.getChildren().add(text);
            stage.show();
    
            TranslateTransition trans = new TranslateTransition(Duration.millis(20_000), text);
            trans.setFromY(500);//500
            trans.setFromZ(-1_000);
            trans.setToY(-500);
            trans.setToZ(1000);//1000
            trans.setInterpolator(Interpolator.LINEAR);
    //        trans.setCycleCount(Animation.INDEFINITE);
            trans.setCycleCount(0);
    
          
            trans.setOnFinished(new EventHandler<ActionEvent>() {
    
                @Override
                public void handle(ActionEvent event) {
                                 text.setText(getCurrentTimeStamp());
                      Task<Void> sleeper = new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    try {
                        Thread.sleep(9500);
    
                    } catch (InterruptedException e) {
                    }
                    return null;
                }
            };
                   sleeper.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
                @Override
                public void handle(WorkerStateEvent event) {
                    
                    trans.pause();
                    try {
                        Thread.sleep(50000);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(TextCrawler.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    trans.play();
                }
            });
                    new Thread(sleeper).start();
                    trans.play();
                }
            });
     
    
            trans.play();
    
        }
    
        public static void main(String... args) {
            launch(args);
        }
    
        public String getCurrentTimeStamp() {
            return new SimpleDateFormat("HH:mm").format(new Date());
        }
    
        private void Runnable() {
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
        }
    }
    
    
    
  • 7

    Die Uhr mit dem Raspi starten:

     sudo nano /etc/rc.local
    
    EDIT:
    
    sudo -u pi sh /home/pi/start.sh
    
    BEFORE:
    
    exit 0

     

 

 

Rpi Headless Osmc, SPI Display mit fbtft im Framebuffer beschreiben RGB 65k

Einfaches Snippet das einen im Binärfile hinterlegten BMP Font(RGB565) im Framebuffer scrollt. Jeder Buchstabe hat in der Datei seinen Startblock Ascii-Code*Width*Heigth und jede Fontgröße wiederum ihren Startblock. (Gepackt mit einem kleinen Tool für Grafiken in SPI-Flashes)

mywindows

Als Grundlage installiert man rpi-update mit integriertem fbtft und aktiviert SPI.

Dann kann man zB. für das kleine HY28B display folgend Treiber laden:

modprobe dma fbtft

modprobe fbtft_device name=hy28b fbtft_device.speed=10000000 fbtft_device.rotate=90 fbtft_device.mode=3

 

 

//#include "font.h"
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <string.h>
#include <fstream>
using namespace std;
unsigned int BinToNum(char* b, int bytes)
{
    unsigned int tmpx = 0;
    unsigned int pw = 1;
    for (int i = 0; i < bytes; i++)
    {
        tmpx += ((unsigned char)b[i] * pw);
        pw = pw * 256;
    }
    return tmpx;
}
int width = 320;
int height = 240;
int ColorBits = 16;
int size = 0;
unsigned short int* matrix;
char* fbp = 0;
int x = 0, y = 0;
long int location = 0;
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;

// Liest MultiImageDatei in Array ein, Buchstaben in verschiedenen Groessen
// jeweils nach Ascii-Code sortiert.
int Open(const char* path)
{
    int pad = 0;
    unsigned int sof = 0;
    unsigned int tx = 0;
    char tmp[4] = { 0, 0, 0, 0 };
    fstream file;
    file.open(path, ios::in);
    if (file.fail())
    {
        width = height = ColorBits = size = 0;
        return -1;
    }
    else
    {
        printf("The Filesizen");
        file.seekg(0, file.end);
        size = file.tellg();
        // Initialize Matrix//
        printf("The Filematrixn");
        matrix = new (unsigned short int[size / 2]);
        sof = 0;
        int counter = 0;
        while (sof < size)
        {
            file.seekg(sof, ios::beg);
            file.read(tmp, (int)(ColorBits / 8));
            tx = BinToNum(tmp, (int)(ColorBits / 8));
            matrix[counter] = tx;
            sof += (int)(ColorBits / 8);
            counter++;
        }
        file.close();
        return 1;
    }
}
// Framebuffer Ausschnitte auf Framebuffer selbst kopieren(Scrolling Ansatz)
int blockcpfb(int source_x, int source_y, int width, int height, int dest_x, int dest_y)
{
    for (int y = 0; y < height; y++)
        for (int x = 0; x < width; x++)
        {
            location = (source_x + x) * (vinfo.bits_per_pixel / 8) + (source_y + y) * 640;

            unsigned short int t = *((unsigned short int*)(fbp + location));
            location = (dest_x + x) * (vinfo.bits_per_pixel / 8) + (dest_y + y) * 640;
            *((unsigned short int*)(fbp + location)) = t;
        }
}
// Kopiert Image/Buchstabe ueber  Startblock aus Tabelle von array in
// Framebuffer
int blockcp(int start, int width, int height, int dest_x, int dest_y)
{
    for (int y = 0; y < height; y++)
        for (int x = 0; x < width; x++)
        {
            location = (dest_x + x) * (vinfo.bits_per_pixel / 8) + (dest_y + y) * 640;
            unsigned short int t = matrix[(y * width) + start + x];

            *((unsigned short int*)(fbp + location)) = t;
        }
}
// schneidet aus dem angegebenem Bildsource aus dem Array einen Bereich aus.
int blockcpclipping(int start, int width, int height, int dest_x, int dest_y, int clipx, int clipy,
    int clipwidth, int clipheight)
{
    for (int y = 0; y < height; y++)
        for (int x = 0; x < width; x++)
        {
            if (x > clipx && y > clipy && x < clipx + clipwidth && y < clipy + clipheight)
            {
                location = (dest_x + x) * (vinfo.bits_per_pixel / 8) + (dest_y + y) * 640;
                unsigned short int t = matrix[(y * width) + start + x];

                *((unsigned short int*)(fbp + location)) = t;
            }
        }
}

// munmap(fbp, screensize);
int main()
{
    // Open the file for reading and writing
    fbfd = open("/dev/fb1", O_RDWR);
    if (fbfd == -1)
    {
        perror("Error: cannot open framebuffer device");
        exit(1);
    }
    printf("The framebuffer device was opened successfully.n");
    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1)
    {
        perror("Error reading fixed information");
        exit(2);
    }
    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1)
    {
        perror("Error reading variable information");
        exit(3);
    }
    printf("%dx%d, %dbppn", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
    // Figure out the size of the screen in bytes
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
    // Map the device to memory
    fbp = (char*)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
    if ((int)fbp == -1)
    {
        perror("Error: failed to map framebuffer device to memory");
        exit(4);
    }
    printf("The framebuffer device was mapped to memory successfully.n");
    x = 320;
    y = 240;
    // Where we are going to put the pixel
    // Figure out where in memory to put the pixel
    if (Open("font.bin") == 1)
    {
    }

    string s = " Guns n Roses ";
    int z = 0;

    for (int c = 0; c <= ((int)s.length() - 4); c++)
    {
        string tmp = s.substr(c, 4);
        string::iterator i;

        for (i = tmp.begin(); i != tmp.end(); i++)
        {
            // berechnet Startblock aus Ascii Code und kopiert Buchstaben.
            blockcp((8330 * (((int)*i) - 32)), 70, 119, 40 + z * 70, 0);
            z++;
        }
    }
    int charspalte = 1;
    int mychar = 0;
    z = 70;
    char tmp = s.at(mychar);
    while (1)
    {
        blockcpfb(1, 0, 319, 119, 0, 0);
        //Einzelne Linien dem Scroll folgend einkopieren(Neuer Buchstabe erscheint komplett)
        // blockcpclipping( (8330*(((int)tmp)-32)),70, 119,320,
        // 0,charspalte,0,1,119);

        if (z == 70)
        {
            char tmp = s.at(mychar);
            blockcp((8330 * (((int)tmp) - 32)), 70, 119, 320 - 70, 0);
            z = 0;
            mychar++;
            charspalte = 1;
            if (mychar == s.length())
            {
                mychar = 0;
            }
        }
        else
        {
            charspalte++;
            z++;
        }
    }

    close(fbfd);
    return 0;
}

APM, Arducopter Mehrkanal Pid tuning.

Dieses Projekt nutzt einen Arduino mit nrf24l01 als Empfänger für einen Sender mit 3 Potis (Siehe Bild) um die Potistellungen in fertige PID Werte umzuwandeln und als I2C Slave über den USERCODE des Arducopter zur Verfügung zu stellen.

7272241428662241465

Hier nur mit cc3d, ein Arduino Empfänger über i2C am FCU-Board mit 3 Kanal Sender:

Potiwerte via nrf24l01 in ppm[] als i2c-Slave request Antwort

float low_P_value = 0.15;
float high_P_value = 0.2;
float low_I_value = 0.002;
float high_I_value = 0.010;
float low_D_value = 0.0;
float high_D_value = 0.5;


void requestEvent()
{
  ppm[0] = (int)(fscale( 1100, 1900, low_P_value, high_P_value, ppm[0], 0.0) * 1000);
  ppm[1] = (int)(fscale( 1100, 1900, low_I_value, high_I_value, ppm[1], 0.0) * 1000);
  ppm[2] = (int)(fscale( 1100, 1900, low_D_value, high_D_value, ppm[2], 0.0) * 1000);
  char check[6];

  check[0] = (ppm[0] >> 8);
  check[1] = (ppm[0] & 255);
  check[2] = (ppm[1] >> 8);
  check[3] = (ppm[1] & 255);
  check[4] = (ppm[2] >> 8);
  check[5] = (ppm[2] & 255);
  Wire.write(check, 6);
}

Usercode.pde Related Code

void userhook_SuperSlowLoop()
{
uint8_t check[6];
// hal.console->print("USERHOOK\n");
    if (failsafe.radio || failsafe.radio_counter != 0) {
        return;
    }
if(g.radio_tuning > 0){
   hal.scheduler->delay(1);

    _i2c_sem = hal.i2c->get_semaphore();
    if (!_i2c_sem->take(HAL_SEMAPHORE_BLOCK_FOREVER)) {
      //  hal.scheduler->panic(PSTR("Failed to get Atmega328p"));
    }
    if(hal.i2c->read(0x99, (int8_t)6, check) != 0)
{

}else
{
 int16_t ch1_value = check[0] << 8 | check[1];
 int16_t ch2_value = check[2] << 8 | check[3];
 int16_t ch3_value = check[4] << 8 | check[5];

   
float ch1f_value= (float) ch1_value/ 1000.0f;
float ch2f_value= (float) ch2_value/ 1000.0f;
float ch3f_value= (float) ch3_value/ 1000.0f;
  hal.console->print("P");
hal.console->print(ch1f_value);
  hal.console->print("I");
hal.console->print(ch2f_value);
  hal.console->print("D");
hal.console->print(ch3f_value);
  hal.console->print("\n");

        g.pid_rate_roll.kP(ch1f_value);
        g.pid_rate_pitch.kP(ch1f_value);
        g.pid_rate_roll.kI(ch2f_value);
        g.pid_rate_pitch.kI(ch2f_value);
        g.pid_rate_roll.kD(ch3f_value);
        g.pid_rate_pitch.kD(ch3f_value);
       

}
 _i2c_sem->give();
}
}
#endif

 

 

In der Usercode Datei wird sobald im Missionplanner ein beliebiges CH6 Tuning gesetzt wird alle 3 Sekunden beliebig viele über Poti gesteuerte PID-Werte gesetzt.

Codebeispiele:

 

cc3d PID TUNING BOX

Dieses Projekt nutzt den 2ten Receiver-Input Anschluß eines cc3d Atom zur Einspeisung von PID-Tx Kanälen. An meinem cc3d nutze ich 6PWM Inputs für meine Fernsteuerung, der trick ist den zweiten unbenutzen PPM Input über 2 Arduinos und eine beliebige Funkstrecke als „TX-Pid Bypass“ zu nutzen.

Anleitung: https://hackaday.io/project/5212-diy-3channel-ppm-quadcopter-calibration-box

So sieht dann das Ergebnis aus:

7272241428662241465

Hier sieht man einen eigenen ProMini Clone mit nrf24l01 Platine und ppm Output am Flexiport. Dazu noch den Sender mit 3Potis der die Werte in 1100-1900 sendet.

Erklärungsvideo:

auf Wunsch kann ich das Projekt zur Weitergabe Beautifiern!