Konwersja kodu z Pythona na Java - sprawdzenie

Konwersja kodu z Pythona na Java - sprawdzenie
Burdzi0
  • Rejestracja:około 9 lat
  • Ostatnio:12 dni
  • Lokalizacja:Futurama
  • Postów:887
0

Witajcie!
Mam ostatnio (mówiąc ostatnio mam na myśli 2 tygodnie) bardzo duży problem z moim wyświetlaczem LCD do Raspberry Pi 3.
Żaden znaleziony w necie kod nie działa, wszelkie próby napisania go własnoręcznie legły w gruzach. Pojawił się jednak promyk nadziei - mam kod w Pythonie, który działa. Spróbowałem go przepisać na Javę i spróbować go wykonać.

Hardware

Software
Działający kod w Pythonie

Kopiuj
#!/usr/bin/python

import smbus
import time

# Define some device parameters
I2C_ADDR = 0x3f  # I2C device address
LCD_WIDTH = 16  # Maximum characters per line

# Define some device constants
LCD_CHR = 1  # Mode - Sending data
LCD_CMD = 0  # Mode - Sending command

LCD_LINE_1 = 0x80  # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0  # LCD RAM address for the 2nd line
LCD_LINE_3 = 0x94  # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4  # LCD RAM address for the 4th line

LCD_BACKLIGHT = 0x08  # On
# LCD_BACKLIGHT = 0x00  # Off

ENABLE = 0b00000100  # Enable bit

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

# Open I2C interface
# bus = smbus.SMBus(0)  # Rev 1 Pi uses 0
bus = smbus.SMBus(1)  # Rev 2 Pi uses 1


def lcd_init():
    # Initialise display
    lcd_byte(0x33, LCD_CMD)  # 110011 Initialise
    lcd_byte(0x32, LCD_CMD)  # 110010 Initialise
    lcd_byte(0x06, LCD_CMD)  # 000110 Cursor move direction
    lcd_byte(0x0C, LCD_CMD)  # 001100 Display On,Cursor Off, Blink Off
    lcd_byte(0x28, LCD_CMD)  # 101000 Data length, number of lines, font size
    lcd_byte(0x01, LCD_CMD)  # 000001 Clear display
    time.sleep(E_DELAY)


def lcd_byte(bits, mode):
    # Send byte to data pins
    # bits = the data
    # mode = 1 for data
    #        0 for command

    bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
    bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT

    # High bits
    bus.write_byte(I2C_ADDR, bits_high)
    lcd_toggle_enable(bits_high)

    # Low bits
    bus.write_byte(I2C_ADDR, bits_low)
    lcd_toggle_enable(bits_low)


def lcd_toggle_enable(bits):
    # Toggle enable
    time.sleep(E_DELAY)
    bus.write_byte(I2C_ADDR, (bits | ENABLE))
    time.sleep(E_PULSE)
    bus.write_byte(I2C_ADDR, (bits & ~ENABLE))
    time.sleep(E_DELAY)


def lcd_string(message, line):
    # Send string to display

    message = message.ljust(LCD_WIDTH, " ")

    lcd_byte(line, LCD_CMD)

    for i in range(LCD_WIDTH):
        lcd_byte(ord(message[i]), LCD_CHR)


def main():
    # Main program block

    # Initialise display
    lcd_init()

    while True:
        # Send some test
        lcd_string("RPiSpy         <", LCD_LINE_1)
        lcd_string("I2C LCD        <", LCD_LINE_2)

        time.sleep(3)

        # Send some more text
        lcd_string(">         RPiSpy", LCD_LINE_1)
        lcd_string(">        I2C LCD", LCD_LINE_2)

        time.sleep(3)


if __name__ == '__main__':

    try:
        main()
    except KeyboardInterrupt:
        pass
    finally:
        lcd_byte(0x01, LCD_CMD)

Prawie działający kod w Javie:

Kopiuj
package com.burdzi0;

import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;
import com.pi4j.io.i2c.I2CFactoryProviderRaspberry;
import com.sun.org.apache.bcel.internal.generic.I2C;

import java.io.IOException;

public class LCDController {

    int I2C_ADDR = 0x3f;
    int LCD_WIDTH = 16;

    int LCD_CHR = 1;
    int LCD_CMD = 0;

    int LCD_LINE_1 = 0x80;
    int LCD_LINE_2 = 0xC0;

    int LCD_BACKLIGHT_ON = 0x08;
    int LCD_BACKLIGHT_OFF = 0x00;

    int ENABLE = 0b00000100;

    int E_PULSE = 5;
    int E_DELAY = 5;

    I2CBus bus;
    I2CDevice dev;

    public LCDController() throws IOException, InterruptedException {
        bus = I2CFactory.getInstance(I2CBus.BUS_1);
        dev = bus.getDevice(I2C_ADDR);
        init();
    }

    void init() throws InterruptedException, IOException {
        lcd_byte(0x33, LCD_CMD);
        lcd_byte(0x32, LCD_CMD);
        lcd_byte(0x06, LCD_CMD);
        lcd_byte(0x0C, LCD_CMD);
        lcd_byte(0x28, LCD_CMD);
        lcd_byte(0x01, LCD_CMD);
        Thread.sleep(E_DELAY);
    }

    void lcd_byte(int bits, int mode) throws IOException, InterruptedException {
        int bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT_ON;
        int bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT_ON;

        dev.write(I2C_ADDR, (byte) bits_high);
        lcd_toggle_enable((byte) bits_high);

        dev.write(I2C_ADDR, (byte) bits_low);
        lcd_toggle_enable((byte) bits_low);
    }

    void lcd_toggle_enable(byte bits) throws InterruptedException, IOException {
        Thread.sleep(E_DELAY);
        dev.write(I2C_ADDR, (byte) (bits | ENABLE));
        Thread.sleep(E_PULSE);
        dev.write(I2C_ADDR, (byte) (bits & ~ENABLE));
        Thread.sleep(E_DELAY);
    }

    void lcd_string(String message, int line) throws IOException, InterruptedException {
        lcd_byte(line, LCD_CMD);
        for (int i = 0; i < LCD_WIDTH; i++) {
            lcd_byte(message.charAt(i), LCD_CHR);
        }
    }

    public static void main(String[] args) {
        LCDController lcd = null;
        try {
            lcd = new LCDController();
            lcd.init();
            while (true) {
                lcd.lcd_string("RPiSpy         <", lcd.LCD_LINE_1);
                lcd.lcd_string("I2C LCD        <", lcd.LCD_LINE_2);
                Thread.sleep(3000);
                lcd.lcd_string(">         RPiSpy", lcd.LCD_LINE_1);
                lcd.lcd_string(">        I2C LCD", lcd.LCD_LINE_2);
                Thread.sleep(3000);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                lcd.lcd_byte(0x01, lcd.LCD_CMD);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

Wiem, że kod nie jest ładny, ale ma być przede wszystkim funkcjonalny. Efektem tego kodu jest:
IMG_20170913_123509.jpgIMG_20170913_123512.jpgIMG_20170913_123514.jpg
Gdzie popełniłem błąd?


Bite my shiny metal ass!
Life throws you an error code like that, you don't have the luxury of a ZnVja2luZw== pop-up explanation *Robię projekty studenckie, pisz priv ;) *
Shalom
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

Bierzesz pod uwagę ze int javie jest signed? Może druga strona oczekuje że jednak unsigned?


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
Burdzi0
  • Rejestracja:około 9 lat
  • Ostatnio:12 dni
  • Lokalizacja:Futurama
  • Postów:887
0

@Shalom: Co w takim wypadku byś zaproponował? long nie mogę użyć, bo metoda oczekuje int. Jak to obejść?


Bite my shiny metal ass!
Life throws you an error code like that, you don't have the luxury of a ZnVja2luZw== pop-up explanation *Robię projekty studenckie, pisz priv ;) *
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
0
Burdzi0 napisał(a):

@Shalom: Co w takim wypadku byś zaproponował? long nie mogę użyć, bo metoda oczekuje int. Jak to obejść?

Nie jestem Shalom, ale mała podpowiedź: "char" jest nieznakowanym 16-bitowym integerem w Javie.

Burdzi0
  • Rejestracja:około 9 lat
  • Ostatnio:12 dni
  • Lokalizacja:Futurama
  • Postów:887
0

Próbowałem zmienić i nic. Wróciłem do kodu i nic. Ewidentnie coś innego wywoływało poprzedni stan. Teraz wygląda na to, że mój kod nie działa wcale :/


Bite my shiny metal ass!
Life throws you an error code like that, you don't have the luxury of a ZnVja2luZw== pop-up explanation *Robię projekty studenckie, pisz priv ;) *
Burdzi0
  • Rejestracja:około 9 lat
  • Ostatnio:12 dni
  • Lokalizacja:Futurama
  • Postów:887
0

Jakieś pomysły? Cokolwiek? Jestem w kropce, a żaden z przykładów w necie nie pomaga...


Bite my shiny metal ass!
Life throws you an error code like that, you don't have the luxury of a ZnVja2luZw== pop-up explanation *Robię projekty studenckie, pisz priv ;) *
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
1

Zrób to metodą "dupa debuging" (po angielsku "tracing").
Dla każdego bajtu który fizycznie wychodzi na urządzenie zapisz czas względny od momentu startu apki (w ms lub ns) i wartość bajtu.
Zrób to po stronie Pythona i Javy.
Porównaj wyniki.
Popraw Jave.

Czyli tutaj - zastąp "dev.write" swoją metodą "lcd_write" i tam umieść "dev.write" oraz tracing.

edytowany 3x, ostatnio: vpiotr
jarekczek
  • Rejestracja:prawie 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Siemianowice Śląskie
  • Postów:500
1

Dumpy porób z obu programów i porównaj. Np. wszystkie wywołania write_byte.


Przeważnie ignoruję niezarejestrowanych użytkowników.
Wibowit
  • Rejestracja:około 20 lat
  • Ostatnio:3 minuty
1

Zamiast patrzeć się w kod w nieskończoność zrób testy jednostkowe.

Kiedyś pracowałem nad komunikacją (przez Ethernet) z czytnikiem kart płatniczych i dzięki testom jednostkowym wykryłem parę błędów w kodzie zanim w ogóle dostałem sprzęt do ręki.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit
S9
W sumie to chyba takie testy nie sa łatwe do napisania (gdzie jest komunikacja, operacje IO a nie jakas logika biznesowa)
Wibowit
Wręcz przeciwnie. Wystarczy, że na czas testu jednostkowego podmienię dev.write na listaIntów.add, a na końcu testu porównam wynikową listę z predefiniowaną i już mam podstawowy test. Nieco trudniej by było gdybyśmy sprawdzali też jednocześnie odstępy czasowe, ale tylko nieco. Wtedy można by zrobić listę przechowującą jakiś typ podstawowy np Wrapped mający dwa podtypy WrappedData i WrappedDelay, a potem można porównywać nagrane wartości z predefiniowanymi tak samo jak w wariancie podstawowym. Banalne byłoby też przeniesienie takich testów z Pythona do Javy.
Burdzi0
@Wibowit: Brzmi fajnie, ale z moimi umiejętnościami niemożliwe do stworzenia. Mam sprawdzać czy coś się wyświetla? Mogę to zrobić tylko ręcznie. Wystarczy, że na czas testu jednostkowego podmienię dev.write na listaIntów.add, a na końcu testu porównam wynikową listę z predefiniowaną i już mam podstawowy test. w tym wypadku nie mam czegoś takiego jak lista predefiniowana, brodzę po ciemku w tym momencie, w każdym razie dzięki za pomysł ;)
Wibowit
Skoro w Pythonie działa dobrze, to nagraj listy do testów w Pythonie, przenieś testy do Javy i odpal je.
vpiotr
@Burdzi0: To nie byłoby takie trudne. Jakbyś zrobił taką samą rejestrację (tracing) w obu językach to w Pythonie zapisujesz ten trace do pliku, w Javie go wczytujesz i porównujesz z tym co Ci wyszło w trakcie testu w Javie. Jak zrobisz plik CSV to będzie to w miarę proste (liczba-porządkowa, adres, wartość). Tam masz trochę tych przeliczanek tych bajtów. Poza tym na początku testuj na prostszych danych - np. jeden znak.
0

Jak nie poradzisz sobie z tym, to zrób niskopoziomową obsługę w c/c++ na pewno będzie więcej przykładów, opakuj to w jni native, a logikę napisz w javie.

Burdzi0
  • Rejestracja:około 9 lat
  • Ostatnio:12 dni
  • Lokalizacja:Futurama
  • Postów:887
2

Matkomoja działa :D
2 tygodnie chrzanienia się:

  • ze sposobem połączenia
  • ze sposobem debugowania
  • z różnymi znaleźnymi kodami
    i wreszcie działa :D

Problem polegał na zmianie:

Kopiuj
dev.write(I2C_ADDR, (byte) bits_high);

na:

Kopiuj
dev.write(LCD_LINE_1, (byte) bits_high);

Cały przykładowy kod wygląda teraz tak:

Kopiuj
import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;
import org.apache.log4j.Logger;

import java.io.IOException;
 
public class LCDController {
 
    int I2C_ADDR = 0x3f;
    int LCD_WIDTH = 16;
 
    int LCD_CHR = 1;
    int LCD_CMD = 0;
 
    int LCD_LINE_1 = 0x80;
    int LCD_LINE_2 = 0xC0;
 
    int LCD_BACKLIGHT_ON = 0x08;
    int LCD_BACKLIGHT_OFF = 0x00;
 
    int ENABLE = 0b00000100;
 
    int E_PULSE = 5;
    int E_DELAY = 5;
 
    I2CBus bus;
    I2CDevice dev;

    public LCDController() throws IOException, InterruptedException, I2CFactory.UnsupportedBusNumberException {
        bus = I2CFactory.getInstance(I2CBus.BUS_1);
        dev = bus.getDevice(I2C_ADDR);
        init();
    }
 
    void init() throws InterruptedException, IOException {
        lcd_byte(0x33, LCD_CMD);
        lcd_byte(0x32, LCD_CMD);
        lcd_byte(0x06, LCD_CMD);
        lcd_byte(0x0C, LCD_CMD);
        lcd_byte(0x28, LCD_CMD);
        lcd_byte(0x01, LCD_CMD);
        Thread.sleep(E_DELAY);
    }
 
    void lcd_byte(int bits, int mode) throws IOException, InterruptedException {
        int bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT_ON;
        int bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT_ON;

        dev.write(LCD_LINE_1, (byte) bits_high);
        lcd_toggle_enable((byte) bits_high);
 
        dev.write(LCD_LINE_1, (byte) bits_low);
        lcd_toggle_enable((byte) bits_low);
    }
 
    void lcd_toggle_enable(byte bits) throws InterruptedException, IOException {
        dev.write(LCD_LINE_1, (byte) (bits | ENABLE));
        dev.write(LCD_LINE_1, (byte) (bits & ~ENABLE));
    }
 
    void lcd_string(String message, int line) throws IOException, InterruptedException {
        lcd_byte(line, LCD_CMD);
        for (int i = 0; i < LCD_WIDTH; i++) {
            lcd_byte(message.charAt(i), LCD_CHR);
        }
    }
 
    public static void main(String[] args) {
        LCDController lcd = null;
        try {
            lcd = new LCDController();
            lcd.init();
            lcd.lcd_string("4programmers.net", lcd.LCD_LINE_1);
            lcd.lcd_string("     Burdzi0    ", lcd.LCD_LINE_2);
            Thread.sleep(2000);
            lcd.lcd_string("Bardzo  dziekuje", lcd.LCD_LINE_1);
            lcd.lcd_string("  za  pomoc  :D ", lcd.LCD_LINE_2);
            Thread.sleep(2000);
            lcd.lcd_string("     Shalom     ", lcd.LCD_LINE_1);
            lcd.lcd_string("     vpiotr     ", lcd.LCD_LINE_2);
            Thread.sleep(2000);
            lcd.lcd_string("    jarekczek   ", lcd.LCD_LINE_1);
            lcd.lcd_string("     Wibowit    ", lcd.LCD_LINE_2);
            Thread.sleep(2000);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (I2CFactory.UnsupportedBusNumberException e) {
            e.printStackTrace();
        } finally {
            try {
                lcd.lcd_byte(0x01, lcd.LCD_CMD);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
}

Przykład podziękowań w zipie ;)
VID_20170913_184414.mp4.zip


Bite my shiny metal ass!
Life throws you an error code like that, you don't have the luxury of a ZnVja2luZw== pop-up explanation *Robię projekty studenckie, pisz priv ;) *
edytowany 1x, ostatnio: Burdzi0
Zobacz pozostałe 2 komentarze
vpiotr
A tak ogólnie to zazdraszczam projektu, bo nie dość że Raspberry Pi to jeszcze kawałek elektroniki.
Burdzi0
@vpiotr: W planach mam napisanie mini gry 2d i zrobienie kontrolerów na bazie GPIO ;)
Burdzi0
@vpiotr: Swoją drogą to nie aż tak drogie, zwłaszcza, że nie trzeba a kupować wszystkiego naraz
vpiotr
@Burdzi0: no tak, ale raczej mi brakuje czasu niż pieniędzy na to.
Burdzi0
@vpiotr: Rozumiem, mam ciągle wakacje i nie mogę się przestawić na to, że inni ni, wybacz :|
Burdzi0
  • Rejestracja:około 9 lat
  • Ostatnio:12 dni
  • Lokalizacja:Futurama
  • Postów:887
0
Kopiuj
    void lcd_byte(int bits, int mode) throws IOException, InterruptedException {
        int bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT_ON;
        int bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT_ON;

        dev.write((byte) bits_high);
        lcd_toggle_enable((byte) bits_high);
 
        dev.write((byte) bits_low);
        lcd_toggle_enable((byte) bits_low);
    }
 
    void lcd_toggle_enable(byte bits) throws InterruptedException, IOException {
        dev.write((byte) (bits | ENABLE));
        dev.write((byte) (bits & ~ENABLE));
    }

Taka zmiana kodu pozwala na przyspieszenie zmiany tekstu - nie ma opóźnienia


Bite my shiny metal ass!
Life throws you an error code like that, you don't have the luxury of a ZnVja2luZw== pop-up explanation *Robię projekty studenckie, pisz priv ;) *
jarekczek
  • Rejestracja:prawie 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Siemianowice Śląskie
  • Postów:500
0

To można fajnie rozpracować bez Pi. Na początku zamiast import wpisać:

Kopiuj
class smbus:
  def __init__(self):
    pass
  @staticmethod
  def SMBus(i):
    return smbus()
  def write_byte(self, a, b):
    print a, b

class time:
  def __init__(self):
    pass
  @staticmethod
  def sleep(i):
    pass

i jeszcze jeden break w while True

A Pythona też trzeba umieć bo używa go Oracle w Oracle'u. :)
Edit: Nie, nie w Oracle'u. W WebLogicu. Web Logic Scripting Tool.


Przeważnie ignoruję niezarejestrowanych użytkowników.
edytowany 1x, ostatnio: jarekczek

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.