1
0
mirror of https://github.com/co-dan/ocaml-wiringpi/ synced 2025-12-16 05:53:51 +01:00

8 Commits

Author SHA1 Message Date
dan
720039b78a explain the initialization sequence 2019-06-29 17:07:24 +02:00
dan
c2ced8a13c A simple Lwt server for delivering the messages to the LCD 2019-06-26 18:30:21 +02:00
dan
feb2e218dc Split the LCD API and the example into two files 2019-06-26 17:22:27 +02:00
dan
2fcb2160f3 Adafruit LCD example 2019-06-26 17:19:35 +02:00
dan
0beb92d299 add an example 2019-02-09 23:43:08 +01:00
dan
5b9eb6b429 typed interface for the GPIO pins on RPI 3 2019-02-09 17:54:26 +01:00
Marek Kubica
fb03eeee58 Merge pull request #4 from co-dan/master
Fix the `ocamlwiring_pull_up_dn_control` stub
2019-01-27 14:06:18 +01:00
1e32b9112a Fix the ocamlwiring_pull_up_dn_control stub 2019-01-27 13:58:05 +01:00
14 changed files with 495 additions and 47 deletions

16
.gitignore vendored
View File

@@ -1,4 +1,12 @@
/_build/
/_opam/
.merlin
*.install
*.mllib
*.mldylib
*.clib
META
Makefile
configure
_tags
setup.ml
setup.data
setup.log
myocamlbuild.ml
_build/

View File

@@ -1,11 +0,0 @@
.PHONY: all
all:
jbuilder build --dev
.PHONY: clean
clean:
jbuilder clean
.PHONY: test
test:
jbuilder runtest --force

35
_oasis Normal file
View File

@@ -0,0 +1,35 @@
OASISFormat: 0.4
Name: WiringPi
Version: 0.0.1
Synopsis: Binding to the WiringPi library for hardware access
Authors: Tobias Bora, Marek Kubica
License: LGPL-3 with OCaml linking exception
Plugins: META (0.4), DevFiles (0.4)
Library "WiringPi"
Path: src/
BuildTools: ocamlbuild
Modules: WiringPi, Gpio3
CSources: WiringPi_stubs.c
CCLib: -lwiringPi
Executable "wpi-button-example"
Path: examples/
BuildTools: ocamlbuild
MainIs: button.ml
BuildDepends: WiringPi,unix
Executable "adafruit-lcd-example"
Path: examples/
BuildTools: ocamlbuild
MainIs: lcd_example.ml
BuildDepends: WiringPi,unix
Executable "adafruit-lcd-server"
CompiledObject: native
Path: examples/
BuildTools: ocamlbuild
MainIs: lcd_lwt.ml
BuildDepends: WiringPi,unix,lwt,lwt.unix,threads
CCLib: -lwiringPi

22
examples/button.ml Normal file
View File

@@ -0,0 +1,22 @@
open Gpio3
(* Set up the pins the following way: *)
(* pin 3 (pull-up) -> btn -> resistor -> gnd *)
let setup () =
Gpio3.setup ();
pin_mode GPIO3 IN;
pull_up_dn_control GPIO3 UP
let rec loop () =
(match digital_read GPIO3 with
| LOW -> print_endline "-- LOW"
| HIGH -> print_endline "++ HIGH");
Unix.sleepf 0.5;
loop ()
let _ =
setup ();
loop ();

125
examples/lcd.ml Normal file
View File

@@ -0,0 +1,125 @@
(** An example with Adafruit character LCD, HD44780. *)
(* The documentation: https://cdn-shop.adafruit.com/datasheets/HD44780.pdf *)
open Gpio3
(** Parameters *)
type mono_lcd = {
columns: int;
rows: int;
rs: pin;
en: pin;
d4: pin;
d5: pin;
d6: pin;
d7: pin
}
(** MAGIC NUMBERS *)
(* Commands *)
let _lcd_cleardisplay = (0x01)
let _lcd_returnhome = (0x02)
let _lcd_entrymodeset = (0x04)
let _lcd_displaycontrol = (0x08)
let _lcd_cursorshift = (0x10)
let _lcd_functionset = (0x20)
let _lcd_setcgramaddr = (0x40)
let _lcd_setddramaddr = (0x80)
(* Entry flags *)
let _lcd_entryleft = (0x02)
let _lcd_entryshiftdecrement = (0x00)
(* Control flags *)
let _lcd_displayon = (0x04)
let _lcd_cursoron = (0x02)
let _lcd_cursoroff = (0x00)
let _lcd_blinkon = (0x01)
let _lcd_blinkoff = (0x00)
(* Move flags *)
let _lcd_displaymove = (0x08)
let _lcd_moveright = (0x04)
let _lcd_moveleft = (0x00)
(* Function set flags *)
let _lcd_4bitmode = (0x00)
let _lcd_2line = (0x08)
let _lcd_1line = (0x00)
let _lcd_5x8dots = (0x00)
(* Offset for up to 4 rows. *)
let _lcd_row_offsets = [|0x00; 0x40; 0x14; 0x54|]
let pulse_enable lcd =
digital_write lcd.en LOW;
(* 1 microsec pause *)
Unix.sleepf(0.0000001);
digital_write lcd.en HIGH;
Unix.sleepf(0.0000001);
digital_write lcd.en LOW;
Unix.sleepf(0.00001)
(** write 8 bits of data *)
let write8 lcd ?(char_mode=false) value =
(* one ms delay to prevent writing too quickly *)
Unix.sleepf(0.001);
let char_mode_value = if char_mode then HIGH else LOW in
digital_write lcd.rs char_mode_value;
let val_of_int i = if i > 0 then HIGH else LOW in
let ival = Char.code value in
(* write the UPPER 4 bits (in reverse order) *)
digital_write lcd.d4 (val_of_int ((ival lsr 4) land 1));
digital_write lcd.d5 (val_of_int ((ival lsr 5) land 1));
digital_write lcd.d6 (val_of_int ((ival lsr 6) land 1));
digital_write lcd.d7 (val_of_int ((ival lsr 7) land 1));
pulse_enable lcd;
(* write the LOWER 4 bits *)
digital_write lcd.d4 (val_of_int (ival land 1));
digital_write lcd.d5 (val_of_int ((ival lsr 1) land 1));
digital_write lcd.d6 (val_of_int ((ival lsr 2) land 1));
digital_write lcd.d7 (val_of_int ((ival lsr 3) land 1));
pulse_enable lcd
(** same as [write8] but write an int (must be within the range) *)
let write8_unsafe lcd ?(char_mode=false) ival =
write8 lcd ~char_mode (Char.unsafe_chr ival)
let setup lcd =
Gpio3.setup ();
pin_mode lcd.rs OUT;
pin_mode lcd.en OUT;
pin_mode lcd.d4 OUT;
pin_mode lcd.d5 OUT;
pin_mode lcd.d6 OUT;
pin_mode lcd.d7 OUT;
(* send the 4-bit initialization sequence: 0011, 0011, 0011, 0010.
see Figure 24 in the docs. *)
write8_unsafe lcd 0x33;
write8_unsafe lcd 0x32;
(* set up some stuff *)
let displayctrl = _lcd_displayon lor _lcd_cursoroff lor _lcd_blinkoff in
let displayfn = _lcd_4bitmode lor _lcd_2line lor _lcd_5x8dots in
let displaymode = _lcd_entryleft lor _lcd_entryshiftdecrement in
write8_unsafe lcd (displayctrl lor _lcd_displaycontrol);
write8_unsafe lcd (displayfn lor _lcd_functionset);
write8_unsafe lcd (displaymode lor _lcd_entrymodeset);
Unix.sleepf(0.003)
let clear lcd =
write8_unsafe lcd _lcd_cleardisplay;
Unix.sleepf(0.003)
let shift_left lcd =
write8_unsafe lcd (_lcd_cursorshift lor _lcd_displaymove lor _lcd_moveleft)
let shift_right lcd =
write8_unsafe lcd (_lcd_cursorshift lor _lcd_displaymove lor _lcd_moveright)
let set_position lcd x y =
let c = x mod lcd.columns in
let r = y mod lcd.rows in
if (c < 0 || r < 0) then failwith "Lcd.set_position: Negative row or column";
write8_unsafe lcd (_lcd_setddramaddr lor (c + _lcd_row_offsets.(r)))
let write_bytes lcd bts =
Bytes.iter (fun c -> write8 lcd c ~char_mode:true) bts

20
examples/lcd_example.ml Normal file
View File

@@ -0,0 +1,20 @@
(** An example with Adafruit character LCD, HD44780. *)
open Gpio3
open Lcd
let lcd = {
columns = 16;
rows = 2;
rs = GPIO20;
en = GPIO16;
d4 = GPIO19;
d5 = GPIO5;
d6 = GPIO11;
d7 = GPIO10;
}
let _ =
setup lcd;
clear lcd;
set_position lcd 0 0;
write_bytes lcd (Bytes.of_string "Hello, world.")

152
examples/lcd_lwt.ml Normal file
View File

@@ -0,0 +1,152 @@
(** An example with Adafruit character LCD, HD44780. *)
open Gpio3
open Lcd
open Lwt
let lcd = {
columns = 16;
rows = 2;
rs = GPIO20;
en = GPIO16;
d4 = GPIO19;
d5 = GPIO5;
d6 = GPIO11;
d7 = GPIO10;
}
(** A nicer abstraction *)
module Cursor = struct
type t = { x: int; y: int; visible: bool; blink: bool; _lcd: mono_lcd }
(** Creates a new cursor at the position (0,0) *)
let of_lcd lcd =
set_position lcd 0 0;
{ x = 0; y = 0; visible = false; blink = false; _lcd = lcd }
(** Explicitly set the position of the cursor. The arguments will be
taken modulo the size of the LCD. *)
let set_position x y cur =
let col = x mod cur._lcd.columns in
let row = y mod cur._lcd.rows in
set_position cur._lcd col row;
{ cur with x = col; y = row }
(** Whether to display the cursor *)
let set_visible flag cur =
let _blinkon = if cur.blink then _lcd_blinkon else _lcd_blinkoff in
let _cursoron = if flag then _lcd_cursoron else _lcd_cursoroff in
let displayctrl = _lcd_displayon lor _cursoron lor _blinkon in
write8_unsafe lcd (displayctrl lor _lcd_displaycontrol);
{ cur with visible = flag }
(** Whether the cursor is blinking *)
let set_blink flag cur =
let _blinkon = if flag then _lcd_blinkon else _lcd_blinkoff in
let _cursoron = if cur.visible then _lcd_cursoron else _lcd_cursoroff in
let displayctrl = _lcd_displayon lor _cursoron lor _blinkon in
write8_unsafe lcd (displayctrl lor _lcd_displaycontrol);
{ cur with blink = flag }
(** Try writing a character, optionally with wrapping. *)
let write_char wrapping v cur =
write8 cur._lcd ~char_mode:true v;
let col = cur.x + 1 in
if not wrapping || col < cur._lcd.columns
then { cur with x = col }
else let row = cur.y + 1 in
let col = 0 in
(* set_position should do the wrapping for us *)
set_position col row cur
let write_bytes wrapping bts cur =
let f cur chr =
if chr = '\n'
then set_position 0 (cur.y + 1) cur
else if chr = '\r'
then set_position 0 cur.y cur
else write_char wrapping chr cur in
Seq.fold_left f cur (Bytes.to_seq bts)
let write_string ?(wrap=false) str cur = write_bytes wrap (Bytes.of_string str) cur
end
(** A useful combinator for the functions from the [Cursor] module *)
let (|>) (m : Cursor.t) (f : Cursor.t -> 'b) = f m
(** Display two lines without wrapping *)
let display_lines l1 l2 =
let col_shift = 3 in
let maxwidth = int_of_float (float_of_int lcd.columns *. 2.5) - col_shift in
let trunc str =
let n = min maxwidth (String.length str) in
String.sub str 0 n
in
let open Cursor in
clear lcd;
of_lcd lcd
|> set_visible false
|> set_blink false
|> set_position col_shift 0
|> write_string (trunc l1) ~wrap:false
|> set_position col_shift 1
|> write_string (trunc l2) ~wrap:false
(** Scrolling thread *)
let rec scroll lcd sleep_for : unit Lwt.t =
shift_left lcd;
Lwt_unix.sleep sleep_for >>= fun () ->
scroll lcd sleep_for
(** Networking stuff *)
let create_socket () =
let open Lwt_unix in
let sock = socket PF_INET SOCK_STREAM 0 in
(* let bind_addr = Unix.inet_addr_loopback in *)
gethostbyname "balthasar.local" >>= fun bind_addr ->
let bind_addr = bind_addr.h_addr_list.(0) in
bind sock @@ ADDR_INET(bind_addr, 8080) >>= fun () ->
listen sock 10;
return sock
let rec handle_message ic oc () =
Lwt_io.read_line_opt ic >>= fun line1 ->
Lwt_io.read_line_opt ic >>= fun line2 ->
match line1,line2 with
| Some l1, Some l2 ->
Lwt_io.printl "Recieved the following message:" >>= fun () ->
Lwt_io.printl l1 >>= fun () ->
Lwt_io.printl l2 >>= fun () ->
Lwt.async (fun () -> Lwt_io.close oc);
ignore (display_lines l1 l2);
return ()
| _,_ -> return ()
let handle_connection conn =
let fd, _ = conn in
let ic = Lwt_io.of_fd Lwt_io.Input fd in
let oc = Lwt_io.of_fd Lwt_io.Output fd in
Lwt.on_failure (handle_message ic oc ())
(fun e -> Printf.printf "Error in `handle_message': %s\n" (Printexc.to_string e));
return ()
let create_server sock =
let rec serve () =
Lwt_unix.accept sock >>= handle_connection >>= serve
in serve
let main () =
Lwt.async (fun () -> scroll lcd 0.7);
create_socket () >>= fun sock ->
create_server sock ()
let _ =
setup lcd;
clear lcd;
ignore (display_lines "Hello, " "world!");
Lwt_main.run @@ main ()

View File

@@ -1,2 +0,0 @@
#use "topfind"
#require "topkg-jbuilder.auto"

95
src/Gpio3.ml Normal file
View File

@@ -0,0 +1,95 @@
(** Typed interface for WiritngPi.
Missing:
- modes PWM_OUTPUT and GPIO_GLOCK
- pwm_write
- digital_write_byte
- analog_read
- analog_write
- delay, delay_microseconds (?)
*)
open WiringPi
let setup () = ignore (setup_gpio ())
type pin =
(* 3V3 | 5V *)
GPIO2 | (* 5V *)
GPIO3 | (* GND *)
GPIO4 | GPIO14
(* GND *) | GPIO15 |
GPIO17 | GPIO18 |
GPIO27 | (* GND *)
GPIO22 | GPIO23
(* 3V3 *) | GPIO24 |
GPIO10 | (* GND *)
GPIO9 | GPIO25 |
GPIO11 | GPIO8
(* GND *) | GPIO7 |
GPIO0 | GPIO1 |
GPIO5 | (* GND *)
GPIO6 | GPIO12 |
GPIO13 | (* GND *)
GPIO19 | GPIO16 |
GPIO26 | GPIO20
let int_of_pin = function
| GPIO2 -> 2
| GPIO3 -> 3
| GPIO4 -> 4
| GPIO14 -> 14
| GPIO15 -> 15
| GPIO17 -> 17
| GPIO18 -> 18
| GPIO27 -> 27
| GPIO22 -> 22
| GPIO23 -> 23
| GPIO24 -> 24
| GPIO10 -> 10
| GPIO9 -> 9
| GPIO25 -> 25
| GPIO11 -> 11
| GPIO8 -> 8
| GPIO7 -> 7
| GPIO0 -> 0
| GPIO1 -> 1
| GPIO5 -> 5
| GPIO6 -> 6
| GPIO12 -> 12
| GPIO13 -> 13
| GPIO19 -> 19
| GPIO16 -> 16
| GPIO26 -> 26
| GPIO20 -> 20
(* What is not supported: PWM_OUTPUT and GPIO_GLOCK *)
type mode = IN | OUT
(** [pin_mode] sets the mode of the pin to either input or output
(other modes not supported at this moment). *)
let pin_mode (p : pin) (m : mode) =
match m with
| IN -> WiringPi.pin_mode (int_of_pin p) 0
| OUT -> WiringPi.pin_mode (int_of_pin p) 1
type pin_updn = UP | DOWN | OFF
(** [pull_up_dn_control] sets the pull-up or pull-down resistor mode on
the given pin, which should be set as an input. *)
let pull_up_dn_control (p : pin) = function
| UP -> WiringPi.pull_up_dn_control (int_of_pin p) 2
| DOWN -> WiringPi.pull_up_dn_control (int_of_pin p) 1
| OFF -> WiringPi.pull_up_dn_control (int_of_pin p) 0
type pin_value = LOW | HIGH
(** [digital_write] the value HIGH or LOW to the given pin which must
have been previously set as an output. *)
let digital_write (p : pin) (v : pin_value) =
match v with
| LOW -> WiringPi.digital_write (int_of_pin p) 0
| HIGH -> WiringPi.digital_write (int_of_pin p) 1
let digital_read (p : pin) : pin_value =
match WiringPi.digital_read (int_of_pin p) with
| 0 -> LOW
| _ -> HIGH

33
src/Gpio3.mli Normal file
View File

@@ -0,0 +1,33 @@
(** broadcom numbers *)
val setup : unit -> unit
type pin =
(* 3V3 | 5V *)
GPIO2 | (* 5V *)
GPIO3 | (* GND *)
GPIO4 | GPIO14
(* GND *) | GPIO15 |
GPIO17 | GPIO18 |
GPIO27 | (* GND *)
GPIO22 | GPIO23
(* 3V3 *) | GPIO24 |
GPIO10 | (* GND *)
GPIO9 | GPIO25 |
GPIO11 | GPIO8
(* GND *) | GPIO7 |
GPIO0 | GPIO1 |
GPIO5 | (* GND *)
GPIO6 | GPIO12 |
GPIO13 | (* GND *)
GPIO19 | GPIO16 |
GPIO26 | GPIO20
type pin_value = LOW | HIGH
type mode = IN | OUT
type pin_updn = UP | DOWN | OFF
val pin_mode : pin -> mode -> unit
val pull_up_dn_control : pin -> pin_updn -> unit
val digital_write : pin -> pin_value -> unit
val digital_read : pin -> pin_value

View File

@@ -45,7 +45,7 @@ value ocamlwiring_pin_mode(value pin, value mode)
value ocamlwiring_pull_up_dn_control(value pin, value pud)
{
CAMLparam2(pin, pud);
pullUpDnControl(Int_val(pin), Int_val(pin));
pullUpDnControl(Int_val(pin), Int_val(pud));
CAMLreturn(Val_unit);
}

View File

@@ -1,3 +0,0 @@
(library
((name wiringpi)
(public_name wiringpi)))

View File

@@ -1,10 +0,0 @@
WiringPi for OCaml, low level Raspberry Pi hardware access
WiringPi is a library that allows easy, Arduino-like access to hardware
functionality (GPIO pins mostly) of the Raspberry Pi minicomputer. That library
is written in C so what this package does is to provide a simple interface to
use the C library from OCaml. The API is unchanged (even the capitalization) to
provide an interface which is as similar as possible to the code that it is
based upon.
Please note that you need to install the WiringPi library first, as it is not
included in this package. Otherwise compilation will fail.

View File

@@ -1,16 +0,0 @@
opam-version: "1.2"
maintainer: "marek@xivilization.net"
homepage: "https://github.com/Leonidas-from-XIV/ocaml-wiringpi"
license: "LGPL-3 with OCaml linking exception"
build: ["jbuilder" "build" "-p" name "-j" jobs]
depends: [
"jbuilder" {build}
]
post-messages: [
"
This package requires WiringPi development files installed.
Tentative instructions : https://gist.githubusercontent.com/Leonidas-from-XIV/a1a7315ac01f7fbee3f0/raw
"
{failure}
]
available: os = "linux"