#+title: Mindwave Emacs * Interface Mindwave with emacs #+begin_src emacs-lisp :tangle yes :padline no ;;; mindwave-emacs.el --- Neurosky mindwave support ;; Copyright (C) 2012 Jonathan Arkell ;; Author: Jonathan Arkell ;; Created: 16 June 2012 ;; Keywords: comint mindwave ;; Version 0.1.6 ;; This file is not part of GNU Emacs. ;; Released under the GPL ;;; Commentary: ;; Please see the org-file that this was generated from. ;;; Code: #+end_src mindwave-emacs is a chunk of elisp code that uses the ThinkGearConnector program to pass off mindwave data directly to emacs. Bundled with it is a data logger, eeg power display and data logger. Everything in this software is pulled from one giant org file. The documentation, source-code... everything. This is all part of a "proof of concept" of being able to write and release elisp all inside of an orgmode file. ** Load the files This chunk of code is handy for loading the elisp files. It is mostly for my own testing. #+begin_src emacs-lisp :tangle no (load-file "./mindwave-emacs.el") (load-file "./gather-into-org.el") (load-file "./solarized-mind.el") (load-file "./mindwave-display.el") (load-file "./23-million-dollar-erisian.el") #+end_src ** Byte Compiling It is highly reccomended that you byte-compile mindwave-emacs. Byte compiling the other files might not be a bad idea either. #+begin_src emacs-lisp :tangle no (byte-compile-file "./mindwave-emacs.el") #+end_src * Changelog - v 0.0 :: - first release on github. - v 0.1.1 :: - rewrite of the hook API. - v 0.1.2 :: - Fix tangle problem with mindwave-debug not being defined. - add example image to repo - v 0.1.3 :: - mindwave display now shows which hooks are currently connected - proper provides on all files - v 0.1.4 :: - fix small provide error in 23-million-dollar-erisian - v 0.1.5 - Added extra info column in 23-million-dollar-erisian. It doesn't have any use yet. - remove raw hook from eeg display - Raw serial connection, and raw eeg gathering finished. - update mw-display/write-values so it uses with-current-buffer and inhibit-read-only - mw-display now shows the total pakets recieved from the serial interface, bad packets, and sample rate - v 0.1.6 - Fix header line of main mindwave emacs - Made mindwave-emacs byte-compileable - mw-display shows a bit of information about the eeg - mw display can show info from mw-info-catcher - mw-info-catcher works - Moved a lot of examples into their own files * Contributing Mindwave emacs needs YOU! Here are a few things that need working on: - Better, reusable gnuplot graphs in data-gather-into-org - Better statistical analysis on the summary table in data-gather-into-org - Tweaks on data-gather-into-org to make it more user-friendly - Low level serial interface into mindwave, so that low-level EEG values can be recorded - better 'mark' interface for delineating incoming stimulus. There might need to be 2 different interfaces, one for self experimenting mad scientists like myself, and one for actual scientists. * Usage Examples and Applications If you want to start playing with mindwave emacs, you will want to have a look at the usage examples. Each example is a fully functional piece of code that you can use for your neurological feedback needs. - gather-in-org :: Pulls eSense (attention/meditation) and eegPower (relative eeg bands) from the mindwave, and puts them into a formatted org table. - solarized-mind :: Displays attention level as the background of emacs. Currently it assumes you are using the solarized-dark theme. The more attentive you are, the lighter blue your background is. - mindwave-display :: throws up a buffer displaying the users current neurological state. (eSense and eegPower) - 23-million-dollar-erisian :: Long term org-based data gathering. Averages your brainwaves over a period of time (30 second increments) and writes it to an org table. ** TODO update application list ** Basic testing code Here is some extremely early testing code that I was using to see if the mindwave code was working. This is provided mostly for informational purposes. For better example code, see the examples below. #+begin_src emacs-lisp :tangle no (mindwave-get-buffer) (mindwave-get-raw nil) (mindwave-get-raw t) (setq mindwave-hook nil) (setq mindwave-raw-hook '()) (setq mindwave-blink-hook '()) (run-hook-with-args 'mindwave-blink-hook "foo") (add-hook 'mindwave-hook 'mindwave-debug-standard) (add-hook 'mindwave-raw-hook 'mindwave-debug-raw) (add-hook 'mindwave-blink-hook 'mindwave-debug-blink) (defun mindwave-debug-standard (o) (message "Standard output: %S" o)) (defun mindwave-debug-raw (o) (message "Raw: %S" o)) (defun mindwave-debug-blink (o) (message "Blink: %S" o)) #+end_src ** TODO turn examples into real code ** Example five: magickal-commit. Store total average brainwave state of a coding session in a magit commit. I am not sure how this is going to play out exactly yet, but this is something that needs to be done. During the editing of a file in a buffer, the attention/meditation level is stored on a per-buffer basis. Before a magit commit, if the buffer/file is part of a repository, it should inject that files average into the commit. Probably in a format like: #+begin_src js :tangle no :export code // developer-state { "eeg" : { "eSense" : { "attention": 60, "meditation": 80 } } } // end-developer-state #+end_src Node that we use json so that it is easily consumable by other tools. Even though everyone knows that sexprs are way cooler. *** Moving and Exponential Averages I'm going to start with the simplest case, which is to get a moving average of the last hour of brain activity. It might also be interesting to play with Exponential Smoothing, but that comes later. https://en.wikipedia.org/wiki/Exponential_smoothing Note that the ring-full hook function will automagickally return an average of the last 30 values. ** Example six: positive re-inforcement when you are in the state of flow. - Show cute/awesome pictures after a sustained state of flow * The code Without much further ado: ** Basic House keeping #+begin_src emacs-lisp :tangle yes (require 'json) (require 'cl) #+end_src *** Debug mode Make this true to enable output on the mindwave buffer #+begin_src emacs-lisp :tangle yes (defvar mindwave-debug nil) #+end_src ** Set Up the client *** Customizable variables #+begin_src emacs-lisp (defgroup 'mindwave-emacs '() "Customizations for the mindwave emacs mode.") #+end_src **** connection Type #+begin_src emacs-lisp :tangle yes (defcustom mindwave-connect-with-serial nil "Whether or not to connect with the serial port." :type 'boolean :group 'mindwave-emacs) #+end_src **** Serial Port connection #+begin_src emacs-lisp :tangle yes (defcustom mindwave-serial-port "/dev/tty.MindWave" "Serial port that the mindwave is connected to." :type 'string :group 'mindwave-emacs) #+end_src **** Poor Signal Level #+begin_src emacs-lisp :tangle yes (defcustom mindwave-poor-signal-level 50 "The signal level that mindwave-emacs should stop running hooks at. The mindwave API sends a poorSignal level hook whenever it senses connection problems. This is generally between 0 and 200. 0 - Best connection 200 - completely off the users head. (get it?)" :type 'integer :group 'mindwave-emacs) #+end_src *** Basic constants **** Thinkgear connector client #+begin_src emacs-lisp :tangle yes (defvar mindwave-host "localhost") (defvar mindwave-port 13854) (defvar mindwave-appName "mindwave-emacs") (defvar mindwave-appKey (sha1 mindwave-appName)) #+end_src **** Raw Serial client #+begin_src emacs-lisp :tangle yes (defconst mindwave-serial-baud 57600) (defconst mindwave-auth-key 0000) #+end_src *** Connection There are 2 different ways to connect to the mind wave. The first way is through the ThinkGear connector, and the second is via a raw serial connection. The ThinkGear connector is high level, slow, and easy to work with, providing JSON data. The raw serial method is low level, fast(?), and a little more difficult. **** Connection variables - Base (both Think Gear Connector and serial) #+begin_src emacs-lisp :tangle yes (defvar mindwave-buffer nil "Variable to store the buffer connected to the process.") (defvar mindwave-process nil "Process that mindwave is connected.") #+end_src **** Return lowlevel connection variables #+begin_src emacs-lisp :tangle yes (defalias 'mindwave-connect 'mindwave-get-buffer) (defun mindwave-get-buffer () "Returns a mindwave buffer. The connection type is dependent on the `mindwvave-connection-type' variable." (if mindwave-connect-with-serial (setq mindwave-buffer (process-buffer (mindwave-make-serial-process))) (mindwave-thinkgear-buffer))) #+end_src ***** ThinkGear Connector According to the documentation of make-comint, if a running process is on the buffer, it is not restarted. So isntead of trying to maintain state, lets just return the existing process that way. #+begin_src emacs-lisp :tangle yes (defun mindwave-thinkgear-buffer () "Returns the buffer for the mindwave connection" (if (and mindwave-process (process-live-p mindwave-process)) mindwave-process (if (progn (setq mindwave-buffer (make-comint "mindwave" (cons mindwave-host mindwave-port))) (setq mindwave-process (get-buffer-process mindwave-buffer)) (save-excursion (set-buffer mindwave-buffer) (buffer-disable-undo mindwave-buffer) (sleep-for 1) (mindwave-authorize) (sleep-for 1) (mindwave-get-raw nil) (sleep-for 1) (add-hook 'comint-preoutput-filter-functions 'mindwave-comint-filter-function nil t)) mindwave-buffer)))) #+end_src ***** Raw Serial We'll be using the commands available here: [[info:elisp#Serial%20Ports]] For now, we're going to assume that only a single serial port will be connected to a mindwave. #+begin_src emacs-lisp :tangle yes (defun mindwave-make-serial-process () "Creates a serial process for mindwave, or returns the current one if it exists. Note that this function assumes that you'll only ever have one mindwave connected." (setq mindwave-serial--bad-packets 0) (setq mindwave-serial--total-packets 0) (if (and mindwave-process (process-live-p mindwave-process)) mindwave-process (setq mindwave-process (apply 'make-serial-process :port mindwave-serial-port :speed mindwave-serial-baud :coding-system 'binary :filter 'mindwave-serial/filter-function :buffer "*mindwave*" '())))) #+end_src Note that since we are in development mode right now, we are not going to detach the buffer yet (but will soon). I think this can be done by using ~:buffer nil~ in the make-serial-process args, but if not, ~(set-process-buffer mindwave-process nil)~ should work. Of course, if we do that, we'll need to make sure the last form on that function is ~mindwave-process~ so it properly returns a process! ** Sending Data #+name: get raw #+begin_src emacs-lisp :tangle yes (defun mindwave-send-string (str) "Helper function to send STRING directly to the mindwave. Please use `mindwave-authorize' or `mindwave-get-raw' for user-level configuration." (if mindwave-connect-with-serial (process-send-string mindwave-process str) (comint-send-string mindwave-process str))) #+end_src ** Recieving Data There are a few ways that you can receive data from mindwave emacs: - hook functions :: Whenever mindwave-emacs receives a particular piece of data, that hook is called with that data. This (for instance) makes it easy to listen to only the attention/meditation levels from the mindwave. - current state :: You can also peek at the last-known values from the mindwave. - brain ring :: mindwave-emacs keeps track of the last 30 results, and stares them in a hook - eeg ring :: If you have raw eeg data turned on, midnwave emacs will store those values in a ring for you, so they are consumable. *** The hooks These hook variables will be cross-connection type, and the arguments will be consistent across both. The following hooks are defined: #+begin_src emacs-lisp :tangle yes (defvar mindwave-hook '() "Hooks to run when mindwave gets standard input\nShould be a in a list that conforms to the json output.") (defvar mindwave-blink-hook '() "Hooks to run when mindwave gets blink input") (defvar mindwave-e-sense-hook '() "Hooks to run when mindwave gets an eSense(tm) reading") (defvar mindwave-eeg-power-hook '() "Hooks to run when mindwave gets an eegPower reading") #+end_src **** ~mindwave-hook~ Called on any input from the mindwave. Note that there are no guarantees about what data will, or won't be available. The argument to the hook function is an alist generally in the format of: #+begin_src emacs-lisp '((poorSignalLevel . 200) (eSense . ((attention . 0) (meditation . 0))) (eegPower . ((delta . 0) (theta . 0) (lowAlpha . 0) (highAlpha . 0) (lowBeta . 0) (highBeta . 0) (lowGamma . 0) (highGamma . 0))) (blinkStrength . 0)) #+end_src **** ~mindwave-blink-hook~ Called when a blink message is received. **** ~mindwave-e-sense-hook~ Called when an eSense message is received (meditation/attention) **** ~mindwave-eeg-power-hook~ Called when eegPower messages are received. **** Low level details for the hooks ***** comint filter function for json style ***** Low Level serial Hook ****** Packet format (header): #+begin_example [SYNC] [SYNC] [PLENGTH] [PAYLOAD...] [CHKSUM] _______________________ _____________ ____________ ^^^^^^^^(Header)^^^^^^^ ^^(Payload)^^ ^(Checksum)^ #+end_example - sync :: 2 bytes total, each being 0xAA - plength :: length of the payload. - payload :: actual message - checksum :: checksum of the packet - all bytes get summed - take the lowest 8 bits perform a bit inverse. - new val should equal checksum ****** Packet format (payload) #+begin_example ([EXCODE]...) [CODE] ([VLENGTH]) [VALUE...] ____________________ ____________ ___________ ^^^^(Value Type)^^^^ ^^(length)^^ ^^(value)^^ #+end_example - excode :: (extended code). Always 0x55. The number of excode bytes determines the extended code level - code :: The data to return. If the code is between 0x00 and 0x7f, it is one byte long, and no length value is sent - 0x02 :: Poor signal quality - 0x04 :: attention eSEnes - 0x05 :: meditation eSense - 0x16 :: Blink Strength - 0x80 :: big-endian 16-bit two's-compliment signed value - 0x83 :: eight big-endian 3-byte unsigned integer values representing EEG band power values - delta, - theta, - low-alpha - high-alpha, - low-beta, - high-beta, - low-gamma, and - mid-gamma - 0xd2 :: unknown - 0xd4 :: unknonw - value :: ****** Serial Filter 39 -- -- -x AA AA LL || end 40 #name: mw-serial-filter-function #+begin_src emacs-lisp :tangle yes ;; In the spirit of 3 strikes and refactor, once you touch this, or mindwave-serial/filter-function ;; make sure to refactor them to a common function. (defun mindwave-serial/filter-function (process output) "Sends input to parser and triggers the hooks." (loop for brain in (mindwave-serial/parse-packets process output) do (mindwave-if-in-list 'blinkStrength brain (mindwave/set-current 'blinkStrength mw-result) (run-hook-with-args 'mindwave-blink-hook mw-result)) (run-hook-with-args 'mindwave-hook brain) (if (and (assoc 'poorSignalLevel brain) (> (cdr (assoc 'poorSignalLevel brain)) mindwave-poor-signal-level)) (progn (when (assoc 'poorSignalLevel brain) (mindwave/set-current 'poorSignalLevel (cdr (assoc 'poorSignalLevel brain))) (run-hook-with-args 'mindwave-poor-signal-hook (cdr (assoc 'poorSignalLevel brain))))) (progn (mindwave-if-in-list 'poorSignalLevel brain (mindwave/set-current 'poorSignalLevel mw-result) (run-hook-with-args 'mindwave-poor-signal-hook mw-result)) (mindwave-if-in-list 'eSense brain (mindwave/set-current 'eSense mw-result) (run-hook-with-args mindwave-e-sense-hook mw-result)) (mindwave-if-in-list 'eegPower brain (mindwave/set-current 'eegPower mw-result) (run-hook-with-args 'mindwave-eeg-power-hook mw-result) (mindwave/brain-ring-update brain))))) (when mindwave-debug (with-current-buffer "*mindwave*" (goto-char (point-max)) (if mindwave-debug (insert output))))) #+end_src #name: mw-serial-parser #+begin_src emacs-lisp :tangle yes (defvar mindwave-serial--partial-packet nil) (defvar mindwave-serial--bad-packets 0) (defvar mindwave-serial--total-packets 0) (defun mindwave-serial/parse-packets (process output) "Lower level serial filter function. Returns a lit of mindwave-emacs compatible lists, with no guarantee of order. Note that this also inserts the raw eeg into the raw eeg ring." (when (not (null mindwave-serial--partial-packet)) (setq output (concat mindwave-serial--partial-packet output)) (setq mindwave-serial--partial-packet nil)) (let ((pos 0) (end (length output)) (dlen 0) (packet '()) (poorSignalLevel '()) (eSense '()) (eegPower '()) (blink '()) (raw nil) (out-packets '())) (setq mindwave-serial--partial-packet (catch 'partial-packet-received (while (< pos end) (when (<= end (+ pos 3)) (setq mindwave-serial--partial-packet (substring output pos)) (throw 'partial-packet-received (substring output pos))) (if (and (= #xAA (aref output pos)) (= #xAA (aref output (+ 1 pos)))) (let ((plen (aref output (+ 2 pos)))) (when (<= end (+ pos 3 plen)) (setq mindwave-serial--partial-packet (substring output pos)) (throw 'partial-packet-received (substring output pos))) (setq mindwave-serial--total-packets (1+ mindwave-serial--total-packets)) (if (and (not (= 0 plen)) (= (aref output (+ 3 plen pos)) (mindwave-serial/checksum-bytestream (substring output (+ pos 3) (+ plen pos 3))))) (mindwave-serial--parse-data) (progn (setq mindwave-serial--bad-packets (1+ mindwave-serial--bad-packets)) (if mindwave-debug (message "Mindwave: Checksum doesn't match, or got a zero length packet. [%s]" (substring output pos (+ 3 plen pos)))) (setq pos (1+ pos))))) (progn (setq pos (1+ pos))))) nil)) (nreverse (delq nil out-packets)))) #+end_src #name: mw-serial-data-parser #+begin_src emacs-lisp :tangle yes (defmacro mindwave-serial--parse-data () '(progn (setq pos (+ 3 pos)) (let ((innerlen (+ pos plen))) (while (< pos innerlen) (if (> #x80 (aref output pos)) (progn (case (aref output pos) (#x02 (add-to-list 'packet (cons 'poorSignalLevel (aref output (+ 1 pos))))) (#x04 (add-to-list 'eSense (cons 'attention (aref output (+ 1 pos))))) (#x05 (add-to-list 'eSense (cons 'meditation (aref output (+ 1 pos))) t)) (#x16 (add-to-list 'packet (cons 'blinkStrength (aref output (+ 1 pos))) t)) (t (message "Mindwave: unknown unibyte type %s with data %s" (aref output pos) (aref output (+ 1 pos))))) (setq pos (+ 2 pos))) (progn (case (aref output pos) (#x80 (setq raw (mindwave-serial/2byte-sword-to-int (aref output (+ 2 pos)) (aref output (+ 3 pos))))) (#x83 (setq eegPower (list (mindwave-serial/make-eeg-list 'delta 2) (mindwave-serial/make-eeg-list 'theta 5) (mindwave-serial/make-eeg-list 'lowAlpha 8) (mindwave-serial/make-eeg-list 'highAlpha 11) (mindwave-serial/make-eeg-list 'lowBeta 14) (mindwave-serial/make-eeg-list 'highBeta 17) (mindwave-serial/make-eeg-list 'lowGamma 20) (mindwave-serial/make-eeg-list 'highGamma 23)))) (208 (message "Mindwave Got Packet with id 208. Full packet: %s" (append output '()))) ;; Not entirely sure what these packets mean (210 (message "Mindwave Got Packet with id 210. Full packet: %s" (append output '()))) ;; (212 (message "Mindwave Got Packet with id 212. Full packet: %s" (append output '()))) ;; (t (message "Mindwave unknown multibyte type %s" (aref output pos)))) (setq pos (+ pos (aref output (+ pos 1)) 2))))) (setq out-packets (cons (append packet (if (not (null eSense)) (cons (cons 'eSense eSense) '())) (if (not (null eegPower)) (cons (cons 'eegPower eegPower) '()))) out-packets)) (if raw (ring-insert mindwave-eeg-ring raw)) (setq raw '() eSense '() eegPower '() packet '() pos (+ pos 1))))) #+end_src #name: mw-serial-parser-tests #+begin_src emacs-lisp :tangle yes (ert-deftest mindwave-serial/null-length-packet-test () "For some reason it looks like the mindwave can throw packets of zero length. Annoying." (should (equal (mindwave-serial/parse-packets nil "ªª���Ï") nil))) (ert-deftest mindwave-serial/checksum-fail-test () "failed checksums should not contain data." (let* ((2nd-data (concat "" (list #x02 #x00 #x04 #x23 #x05 #x46))) (2nd-data-checksum (mindwave-serial/checksum-bytestream 2nd-data)) (packet (concat (list #xAA #xAA #x20 #x02 #x00 #x83 #x18 #x00 #x00 #x94 #x00 #x00 #x42 #x00 #x00 #x0B #x00 #x00 #x64 #x00 #x00 #x4D #x00 #x00 #x3D #x00 #x00 #x07 #x00 #x00 #x05 #x04 #x0D #x05 #x3D #x34) (list #xAA #xAA #x06 #x02 #x00 #x04 #x01 #x05 #x04 #xff) (list #xAA #xAA #x06) 2nd-data (list 2nd-data-checksum)))) (should-equal (mindwave-serial/parse-packets nil packet) '(((poorSignalLevel . 0) (eSense . ((attention . 13) (meditation . 61))) (eegPower . ((delta . #x94) (theta . #x42) (lowAlpha . #x0b) (highAlpha . #x64) (lowBeta . #x4d) (highBeta . #x3d) (lowGamma . #x07) (highGamma . #x05)))) ((poorSignalLevel . 0) (eSense . ((attention . #x23) (meditation . #x46)))))))) (ert-deftest mindwave-serial/partial-packet-test () "Test parital packets being received" (let ((packet1 (concat '(170 170 4 128 2 0 102 23 170 170 4 128 2 0 107 18 170 170 4 128 2 0 96 29 170 170 4 128 2 0))) (packet2 (concat '(71 54 170 170 4 128 2 0 41 84 170 170 4 128 2 0))) (packet3 (concat '(5) (list (mindwave-serial/checksum-bytestream (concat '(128 2 0 5)))) '(170))) (packet4 (concat '(170 4 128 2 0 0) (list (mindwave-serial/checksum-bytestream (concat '(128 2 0 0))))))) (setq mindwave-serial--partial-packet nil) (setq mindwave-eeg-ring (make-ring mindwave-eeg-ring-size)) (should-equal (mindwave-serial/parse-packets nil packet1) '()) (should-equal (delq nil (ring-elements mindwave-eeg-ring)) '(96 107 102)) (should-equal mindwave-serial--partial-packet (concat '(170 170 4 128 2 0))) (should-equal (mindwave-serial/parse-packets nil packet2) '()) (should-equal (delq nil (ring-elements mindwave-eeg-ring)) '(41 71 96 107 102)) (should-equal (mindwave-serial/parse-packets nil packet3) '()) (should-equal (delq nil (ring-elements mindwave-eeg-ring)) '(5 41 71 96 107 102)) (should-equal mindwave-serial--partial-packet (concat '(170))) (should-equal (mindwave-serial/parse-packets nil packet4) '()) (should-equal (delq nil (ring-elements mindwave-eeg-ring)) '(0 5 41 71 96 107 102)) (should-equal mindwave-serial--partial-packet nil))) (ert-deftest mindwave-serial/actual-data-barf-tests () "Sometimes I get barfage from actual data." (mindwave-serial/parse-packets nil "ªª��� ") (mindwave-serial/parse-packets nil "ªª���6Gªª���D9ªª���F7ªª���;Bªª���3Jªª����fªª���ÿí�ªª���ÿè�ªª��� sªª���)Tªª���+Rªª����gªª����iªª���#Zªª����bªª����xªª��� sªª����dªª����wªª���ÿÝ¡ªª���ÿÕ©ªª���ÿù�") (mindwave-serial/parse-packets nil "ªª���6Gªª���D9ªª���F7ªª���;Bªª���3Jªª����fªª���ÿí�ªª���ÿè�ªª��� sªª���)Tªª���+Rªª����gªª����iªª���#Zªª����bªª����xªª��� sªª����dªª����wªª���ÿÝ¡ªª���ÿÕ©ªª���ÿù�ªª��� ")) (ert-deftest mindwave-serial/filter-test () "test the lowlevel serial interface" (should-error (mindwave-serial/parse-packets nil "xx")) (let ((packet (concat (list #xAA #xAA #x20 #x02 #x00 #x83 #x18 #x00 #x00 #x94 #x00 #x00 #x42 #x00 #x00 #x0B #x00 #x00 #x64 #x00 #x00 #x4D #x00 #x00 #x3D #x00 #x00 #x07 #x00 #x00 #x05 #x04 #x0D #x05 #x3D #x34)))) (should-not (null (mindwave-serial/parse-packets nil packet))) (should-equal (mindwave-serial/parse-packets nil packet) '(((poorSignalLevel . 0) (eSense . ((attention . 13) (meditation . 61))) (eegPower . ((delta . #x94) (theta . #x42) (lowAlpha . #x0b) (highAlpha . #x64) (lowBeta . #x4d) (highBeta . #x3d) (lowGamma . #x07) (highGamma . #x05)))))))) (ert-deftest mindwave-serial/filter-test-larger-data () "test the low level serial filter against a larger data set" (let ((packet (concat (list #xAA #xAA #x20 #x02 #x00 #x83 #x18 #x00 #x00 #x94 #x00 #x00 #x42 #x00 #x00 #x0B #x00 #x00 #x64 #x00 #x00 #x4D #x00 #x00 #x3D #x00 #x00 #x07 #x00 #x00 #x05 #x04 #x0D #x05 #x3D #x34)))) (should-equal (mindwave-serial/parse-packets nil (concat packet packet)) '(((poorSignalLevel . 0) (eSense . ((attention . 13) (meditation . 61))) (eegPower . ((delta . #x94) (theta . #x42) (lowAlpha . #x0b) (highAlpha . #x64) (lowBeta . #x4d) (highBeta . #x3d) (lowGamma . #x07) (highGamma . #x05)))) ((poorSignalLevel . 0) (eSense . ((attention . 13) (meditation . 61))) (eegPower . ((delta . #x94) (theta . #x42) (lowAlpha . #x0b) (highAlpha . #x64) (lowBeta . #x4d) (highBeta . #x3d) (lowGamma . #x07) (highGamma . #x05)))))))) (ert-deftest mindwave-serial/filter-test-alternate-data () (let* ((pdata (concat (list #x02 #x00 #x80 #x02 #x00 #x80 #x16 #xff #x83 #x18 #x00 #x00 #x94 #x00 #x00 #x42 #x00 #x00 #x0B #x00 #x00 #x64 #x00 #x00 #x4D #x00 #x00 #x3D #x00 #x00 #x07 #x00 #x00 #x05 #x04 #x0D #x05 #x3D))) (packet (concat (list #xAA #xAA (length pdata)) pdata (list (mindwave-serial/checksum-bytestream pdata))))) (should-equal (mindwave-serial/parse-packets nil packet) '(((poorSignalLevel . 0) (blinkStrength . 255) (eSense . ((attention . 13) (meditation . 61))) (eegPower . ((delta . #x94) (theta . #x42) (lowAlpha . #x0b) (highAlpha . #x64) (lowBeta . #x4d) (highBeta . #x3d) (lowGamma . #x07) (highGamma . #x05)))))))) (ert-deftest mindwave-serial/filter-raw-eeg-test () "test the lowlevel serial interface" (let* ( (pdata1 (concat (list #x02 #x00 #x80 #x02 #x00 #b01111111 #x16 #xff #x83 #x18 #x00 #x00 #x94 #x00 #x00 #x42 #x00 #x00 #x0B #x00 #x00 #x64 #x00 #x00 #x4D #x00 #x00 #x3D #x00 #x00 #x07 #x00 #x00 #x05 #x04 #x0D #x05 #x3D))) (pdata2 (concat (list #x80 #x02 #x00 #x40))) (pdata3 (concat (list #x80 #x02 #x00 #x80))) (pdata4 (concat (list #x80 #x02 #x01 #x00))) (big-packet (concat (list #xAA #xAA (length pdata1)) pdata1 (list (mindwave-serial/checksum-bytestream pdata1)) (list #xAA #xAA (length pdata2)) pdata2 (list (mindwave-serial/checksum-bytestream pdata2)) (list #xAA #xAA (length pdata3)) pdata3 (list (mindwave-serial/checksum-bytestream pdata3)) (list #xAA #xAA (length pdata4)) pdata4 (list (mindwave-serial/checksum-bytestream pdata4)))) (eeg-packet (concat (list #xAA #xAA 4) (list #x80 #x02 0 #x40 (mindwave-serial/checksum-bytestream (list #x80 #x02 0 #x40))) (list #xAA #xAA 4) (list #x80 #x02 0 #x80 (mindwave-serial/checksum-bytestream (list #x80 #x02 0 #x80))) (list #xAA #xAA 4) (list #x80 #x02 #x01 #x00 (mindwave-serial/checksum-bytestream (list #x80 #x02 1 #x00)))))) (setq mindwave-eeg-ring (make-ring mindwave-eeg-ring-size)) (should-equal (mindwave-serial/parse-packets nil big-packet) '(((poorSignalLevel . 0) (blinkStrength . 255) (rawEeg . 127) (eSense . ((attention . 13) (meditation . 61))) (eegPower . ((delta . #x94) (theta . #x42) (lowAlpha . #x0b) (highAlpha . #x64) (lowBeta . #x4d) (highBeta . #x3d) (lowGamma . #x07) (highGamma . #x05)))) ((rawEeg . 64)) ((rawEeg . 128)) ((rawEeg . 256)))) (should-equal (delq nil (ring-elements mindwave-eeg-ring)) ;ring-elements returns the order in newest first format. '(256 128 64 127)) (setq mindwave-eeg-ring (make-ring mindwave-eeg-ring-size)) (should-equal (mindwave-serial/parse-packets nil eeg-packet) '(((rawEeg . 64)) ((rawEeg . 128)) ((rawEeg . 256)))) (should-equal (delq nil (ring-elements mindwave-eeg-ring)) '(256 128 64)) (setq mindwave-eeg-ring (make-ring mindwave-eeg-ring-size)) (mindwave-serial/parse-packets nil big-packet) (mindwave-serial/parse-packets nil eeg-packet) (should-equal (delq nil (ring-elements mindwave-eeg-ring)) '(256 128 64 256 128 64 127)))) #+end_src ****** Helpers for the serial filter Note that these helpers are all defined as macros, so that when mindwave-emacs is compiled it will (theoretically) run faster. #+name: mw-serial-filter-helpers #+begin_src emacs-lisp :result none :tangle yes (defmacro mindwave-serial/make-eeg-list (eegName b1) (let ((b2 (+ 1 b1)) (b3 (+ 2 b1))) `(cons ,eegName (mindwave-serial/3byte-uword-to-int (aref output (+ ,b1 pos)) (aref output (+ ,b2 pos)) (aref output (+ ,b3 pos)))))) (ert-deftest mindwave-serial/make-eeg-list-test () "" (should (equal '(cons 'highGamma (mindwave/3byte-uword-to-int (aref output (+ 23 pos)) (aref output (+ 24 pos)) (aref output (+ 25 pos)))) (macroexpand '(mindwave-serial/make-eeg-list 'highGamma 23))))) (defmacro mindwave-serial/checksum-bytestream (stream) "do a checksum calculation on a bytestream" `(lognot (logior -256 (mod (reduce #'(lambda (x y) (mod (+ x y) 256)) ,stream) 256)))) (ert-deftest mindwave-serial/checksum-test () "test the checksum macro" (should (= (mindwave-serial/checksum-bytestream (concat (list 0))) 255)) (should (= (mindwave-serial/checksum-bytestream (concat (list 255))) 0)) (should (= (mindwave-serial/checksum-bytestream (concat (list 255 1))) 255)) (should (= (mindwave-serial/checksum-bytestream (concat (list 255 1 255 1 255 1))) 255)) (should (= (mindwave-serial/checksum-bytestream (concat (list 0 1))) 254)) (should (= (mindwave-serial/checksum-bytestream (concat (list #x02 #x00 #x83 #x18 #x00 #x00 #x94 #x00 #x00 #x42 #x00 #x00 #x0B #x00 #x00 #x64 #x00 #x00 #x4D #x00 #x00 #x3D #x00 #x00 #x07 #x00 #x00 #x05 #x04 #x0D #x05 #x3D))) #x34))) (defmacro mindwave-serial/2byte-sword-to-int (byte1 byte2) "Perform a 2's compliment on a pair of bytes" `(if (> ,byte1 127) (logior -65536 (+ (* 256 ,byte1) ,byte2)) (+ (* 256 ,byte1) ,byte2))) (ert-deftest mindwave-2s-compliment-test () "Test 2s compliment macros" (should (= (mindwave-serial/2byte-sword-to-int 0 1) 1)) (should (= (mindwave-serial/2byte-sword-to-int 0 2) 2)) (should (= (mindwave-serial/2byte-sword-to-int 0 127) 127)) (should (= (mindwave-serial/2byte-sword-to-int 0 128) 128)) (should (= (mindwave-serial/2byte-sword-to-int 0 #x80) 128)) (should (= (mindwave-serial/2byte-sword-to-int 1 0) 256)) (should (= (mindwave-serial/2byte-sword-to-int #b11111111 #b11111111) -1)) (should (= (mindwave-serial/2byte-sword-to-int #b11111111 #b11111011) -5)) (should (= (mindwave-serial/2byte-sword-to-int #b11111111 #b11000000) -64)) (should (= (mindwave-serial/2byte-sword-to-int #b11111111 #b10000000) -128)) (should (= (mindwave-serial/2byte-sword-to-int #b11111111 #b00000000) -256)) (should (= (mindwave-serial/2byte-sword-to-int #b11000000 #b00000000) -16384)) (should (= (mindwave-serial/2byte-sword-to-int #b10000000 #b00000000) -32768))) (defmacro mindwave-serial/3byte-uword-to-int (byte1 byte2 byte3) `(+ (* #x010000 ,byte1) (* #x000100 ,byte2) ,byte3)) (ert-deftest mindwave-3byte-uword () "Test 3 byte uword macros" (should (= (mindwave-serial/3byte-uword-to-int 0 0 1) 1)) (should (= (mindwave-serial/3byte-uword-to-int 0 1 0) 256)) (should (= (mindwave-serial/3byte-uword-to-int 1 0 0) 65536)) (should (= (mindwave-serial/3byte-uword-to-int 255 255 255) #xffffff))) #+end_src ***** middle layer The middle layer sits between the outgoing ~mindwave-hook~ hooks, and the process hooks. #+begin_src emacs-lisp :tangle yes (eval-when-compile (require 'cl)) (defun mindwave-if-in-list-run-hook (key list hook &rest funcs) (when (assoc key list) (when (not (null funcs)) (dolist (func funcs) (apply func (cdr (assoc key list))))) (run-hook-with-args hook (cdr (assoc key list))))) (defmacro mindwave-if-in-list (key list &rest forms) "Helper macro to bind the mw-result to (assoc KEY LIST) and run FORMS" (declare (indent 2)) `(let ((mw-result (assoc ,key ,list))) (if mw-result (progn (setq mw-result (cdr mw-result)) ,@forms) nil))) (ert-deftest mindwave/test-if-in-list () "" (let ((r nil)) (mindwave-if-in-list 'a '() (setq r 't)) (should (not r))) (let ((r nil)) (debug) (mindwave-if-in-list 'a '((a 1)) (setq r mw-result)) (should r))) ;; In the spirit of 3 strikes and refactor, once you touch this, or mindwave-serial/filter-function ;; make sure to refactor them to a common function. (defun mindwave-comint-filter-function (output) "A helper hook to pass off output to the apropriate hooks" (when (and (stringp output) (string= (substring output 0 1) "{")) (loop for out in (split-string output "\C-j" t) do (let ((brain (json-read-from-string out))) (mindwave-if-in-list 'blinkStrength brain (mindwave/set-current 'blinkStrength mw-result) (run-hook-with-args 'mindwave-blink-hook mw-result)) (run-hook-with-args 'mindwave-hook brain) (if (and (assoc 'poorSignalLevel brain) (> (cdr (assoc 'poorSignalLevel brain)) mindwave-poor-signal-level)) (progn (when (assoc 'poorSignalLevel brain) (mindwave/set-current 'poorSignalLevel (cdr (assoc 'poorSignalLevel brain))) (run-hook-with-args 'mindwave-poor-signal-hook (cdr (assoc 'poorSignalLevel brain))))) (progn (mindwave-if-in-list-run-hook 'rawEeg brain 'mindwave-raw-hook) (mindwave-if-in-list 'poorSignalLevel brain (mindwave/set-current 'poorSignalLevel mw-result) (run-hook-with-args 'mindwave-poor-signal-hook mw-result)) (mindwave-if-in-list 'eSense brain (mindwave/set-current 'eSense mw-result) (run-hook-with-args mindwave-e-sense-hook mw-result)) (mindwave-if-in-list 'eegPower brain (mindwave/set-current 'eegPower mw-result) (run-hook-with-args 'mindwave-eeg-power-hook mw-result) (mindwave/brain-ring-update brain))))))) (if mindwave-debug output "")) #+end_src ****** TODO refactor the comint filter function a little better, especially around signal level *** Current State Shows the current state of the mindwave. Note that there is no guarantees about the freshness of that data. In particular, the blink strength is likely to be quite stale. **** TODO talk about blink strength #+begin_src emacs-lisp :tangle yes :results silent (defvar mindwave/current '((poorSignalLevel . 200) (eSense . ((attention . 0) (meditation . 0))) (eegPower . ((delta . 0) (theta . 0) (lowAlpha . 0) (highAlpha . 0) (lowBeta . 0) (highBeta . 0) (lowGamma . 0) (highGamma . 0))) (blinkStrength . 0)) "The last known values from the mindwave headset.") (defun mindwave/set-current (key val) (setq mindwave/current (list (if (equal key 'poorSignalLevel) (cons key val) (assoc 'poorSignalLevel mindwave/current)) (if (equal key 'eSense) (cons key val) (assoc 'eSense mindwave/current)) (if (equal key 'eegPower) (cons key val) (assoc 'eegPower mindwave/current)) (if (equal key 'blinkStrength) (cons key val) (assoc 'blinkStrength mindwave/current))))) (ert-deftest mindwave/current-setters () (setq mindwave/current '((poorSignalLevel . 200) (eSense . ((attention . 0) (meditation . 0))) (eegPower . ((delta . 0) (theta . 0) (lowAlpha . 0) (highAlpha . 0) (lowBeta . 0) (highBeta . 0) (lowGamma . 0) (highGamma . 0))) (blinkStrength . 0))) (mindwave/set-current 'blinkStrength 255) (should (equal (assoc 'blinkStrength mindwave/current) '(blinkStrength . 255))) (should (equal mindwave/current '((poorSignalLevel . 200) (eSense . ((attention . 0) (meditation . 0))) (eegPower . ((delta . 0) (theta . 0) (lowAlpha . 0) (highAlpha . 0) (lowBeta . 0) (highBeta . 0) (lowGamma . 0) (highGamma . 0))) (blinkStrength . 255)))) (mindwave/set-current 'eegPower '(((delta . 1) (theta . 1) (lowAlpha . 1) (highAlpha . 1) (lowBeta . 1) (highBeta . 1) (lowGamma . 1) (highGamma . 1)))) (should (equal mindwave/current '((poorSignalLevel . 200) (eSense . ((attention . 0) (meditation . 0))) (eegPower . ((delta . 1) (theta . 1) (lowAlpha . 1) (highAlpha . 1) (lowBeta . 1) (highBeta . 1) (lowGamma . 1) (highGamma . 1))) (blinkStrength . 255)))) (mindwave/set-current 'eegPower '(((delta . 2) (theta . 2) (lowAlpha . 2) (highAlpha . 2) (lowBeta . 2) (highBeta . 2) (lowGamma . 2) (highGamma . 2)))) (should (equal mindwave/current '((poorSignalLevel . 200) (eSense . ((attention . 0) (meditation . 0))) (eegPower . ((delta . 2) (theta . 2) (lowAlpha . 2) (highAlpha . 2) (lowBeta . 2) (highBeta . 2) (lowGamma . 2) (highGamma . 2))) (blinkStrength . 0)))) (setq mindwave/current '((poorSignalLevel . 200) (eSense . ((attention . 0) (meditation . 0))) (eegPower . ((delta . 0) (theta . 0) (lowAlpha . 0) (highAlpha . 0) (lowBeta . 0) (highBeta . 0) (lowGamma . 0) (highGamma . 0))) (blinkStrength . 0))) ) #+end_src *** Brain Ring, a ring-storage of the last 30 vals The concept behind the brain ring is to keep a tally of the users neurological state. The structure of the brain ring is similar to the standard mindwave structure. That is to say, an alist of the following format: #+begin_src emacs-lisp :tangle no ((eSense . ((meditation . 40) (attention . 60))) (eegPower . ((delta . 2) (theta . 3) (lowAlpha . 2) (highAlpha . 3) (lowBeta . 2) (highBeta . 3) (lowGamma . 2) (highGamma . 3)))) #+end_src This makes it possible to look at short term trends (and possibly longer term) versus just the second-by-second eeg output. Note, that the new ring stats out empty, but you can use the hook to fill in new values if you want your average to retain some kind of memory. For an example of this, see the solarized-mind example. ***** Internals ****** Set up basic variables #+begin_src emacs-lisp :tangle yes (defconst mindwave/brain-ring-size 30) (defvar mindwave/brain-ring (make-ring mindwave/brain-ring-size)) (defvar mindwave/brain-ring-reset-counter 0) (defvar mindwave/brain-ring-full-hook '() "Hook to call when the brain ring is full") #+end_src ****** Access-in, quick access to ~((eSense . ((meditation . foo) ...)) ...)~ We'll need to access the guts of our rings pretty frequently, so here is a convenience function and a test. #+begin_src emacs-lisp :tangle yes (defun mindwave/access-in (outer-key inner-key list) "Access the value of INNER-KEY from OUTER-KEY of alist LIST" (cdr (assoc inner-key (cdr (assoc outer-key list))))) (ert-deftest mindwave/test-access-in () (should (equal (mindwave/access-in 'outer 'inner '((outer1 . (inner1 . 0)) (outer . ((inner . 23))))) 23))) #+end_src ***** Make a new brain-ring entry from args There are two ways to make a brain ring, either through ~mindwave/make-brain-ring~ which will allow you to set each and every value, or ~mindwave/make-single-val-brain-ring~. ****** Brain Brings #+begin_src emacs-lisp :tangle yes (defun mindwave/make-brain-ring (meditation attention delta theta lowAlpha highAlpha lowBeta highBeta lowGamma highGamma &optional poorSignalLevel) "convenience function to make a valid brain ring entry" `((poorSignalLevel . ,(or poorSignalLevel 0)) (eSense . ((meditation . ,meditation) (attention . ,attention))) (eegPower . ((delta . ,delta) (theta . ,theta) (lowAlpha . ,lowAlpha) (highAlpha . ,highAlpha) (lowBeta . ,lowBeta) (highBeta . ,highBeta) (lowGamma . ,lowGamma) (highGamma . ,highGamma))))) (ert-deftest mindwave/make-brain-ring () "Maker tests. Super simple," (should (equal (mindwave/make-brain-ring 0 0 0 0 0 0 0 0 0 0) (mindwave/make-brain-ring 0 0 0 0 0 0 0 0 0 0 0))) (should (equal (mindwave/make-brain-ring 2 3 5 23 32 46 92 184 7 13 21) (mindwave/make-brain-ring 2 3 5 23 32 46 92 184 7 13 21)))) (defun mindwave/make-single-val-brain-ring (val) "Convenience function to make a brain ring of a single value VAL. Useful for dealing with averages." (mindwave/make-brain-ring val val val val val val val val val val val)) (ert-deftest mindwave/make-brain-ring () "Maker tests. Super simple," (should (equal (mindwave/make-brain-ring 0 0 0 0 0 0 0 0 0 0) (mindwave/make-single-val-brain-ring 0))) (should (equal (mindwave/make-brain-ring 1 1 1 1 1 1 1 1 1 1 1) (mindwave/make-single-val-brain-ring 1)))) ;; Averaging the brain ring can be a little dicey since we expect poorSignalLevel to be 0 (defun mindwave/safe-div (dividend divisor) "Another division function safe to use with averaging. 0 save-div 0 = 0" (if (= 0 divisor) 0 (/ dividend divisor))) (ert-deftest mindwave/safe-div () "test out safe div" (should (equal (mindwave/safe-div 0 0) 0)) (should (equal (mindwave/safe-div 3 0) 0)) (should (equal (mindwave/safe-div 0 3) 0))) #+end_src ***** Run a function with 2 rings as the args This could be expanded to handle multiple args, but no need for that yet. #+begin_src emacs-lisp :tangle yes (defun mindwave/brain-ring-apply (fn ring1 ring2) "Takes the \"brain-rings\" RING1 and RING2 and runs FN on it's guts" (mindwave/make-brain-ring (funcall fn (mindwave/access-in 'eSense 'meditation ring1) (mindwave/access-in 'eSense 'meditation ring2)) (funcall fn (mindwave/access-in 'eSense 'attention ring1) (mindwave/access-in 'eSense 'attention ring2)) (funcall fn (mindwave/access-in 'eegPower 'delta ring1) (mindwave/access-in 'eegPower 'delta ring2)) (funcall fn (mindwave/access-in 'eegPower 'theta ring1) (mindwave/access-in 'eegPower 'theta ring2)) (funcall fn (mindwave/access-in 'eegPower 'lowAlpha ring1) (mindwave/access-in 'eegPower 'lowAlpha ring2)) (funcall fn (mindwave/access-in 'eegPower 'highAlpha ring1) (mindwave/access-in 'eegPower 'highAlpha ring2)) (funcall fn (mindwave/access-in 'eegPower 'lowBeta ring1) (mindwave/access-in 'eegPower 'lowBeta ring2)) (funcall fn (mindwave/access-in 'eegPower 'highBeta ring1) (mindwave/access-in 'eegPower 'highBeta ring2)) (funcall fn (mindwave/access-in 'eegPower 'lowGamma ring1) (mindwave/access-in 'eegPower 'lowGamma ring2)) (funcall fn (mindwave/access-in 'eegPower 'highGamma ring1) (mindwave/access-in 'eegPower 'highGamma ring2)) (funcall fn (cdr (assoc 'poorSignalLevel ring1)) (cdr (assoc 'poorSignalLevel ring2))))) (ert-deftest mindwave/test-brain-ring-add () (should (equal (mindwave/make-brain-ring 0 0 0 0 0 0 0 0 0 0) (mindwave/brain-ring-apply '+ (mindwave/make-brain-ring 0 0 0 0 0 0 0 0 0 0) (mindwave/make-brain-ring 0 0 0 0 0 0 0 0 0 0)))) (should (equal (mindwave/make-brain-ring 1 2 3 4 5 6 7 8 9 10) (mindwave/brain-ring-apply '+ (mindwave/make-brain-ring 1 2 3 4 5 6 7 8 9 10) (mindwave/make-brain-ring 0 0 0 0 0 0 0 0 0 0)))) (should (equal (mindwave/make-brain-ring 2 3 4 5 6 7 8 9 10 11) (mindwave/brain-ring-apply '+ (mindwave/make-brain-ring 1 2 3 4 5 6 7 8 9 10) (mindwave/make-brain-ring 1 1 1 1 1 1 1 1 1 1)))) (should (equal (mindwave/make-brain-ring 2 3 4 5 6 7 8 9 10 11 12) (mindwave/brain-ring-apply '+ (mindwave/make-brain-ring 1 2 3 4 5 6 7 8 9 10 11) (mindwave/make-brain-ring 1 1 1 1 1 1 1 1 1 1 1))))) #+end_src ***** Ring Update This function is called on every eSense/eeg update. When the signal level is good, and the mindwave data contains both eSense and eegPower items, it adds a new entry to the brain ring. When the brain ring is full it clears it out, and calls the brain-ring-full-hook. The size of the brain-ring is 30 items. Note that in so doing, we don't actually have a ring per-sae. We have a new data structure which is a running average of the last 30 In the future, maybe it could ALSO be more hook-like. #+begin_src emacs-lisp :tangle yes (defun mindwave/brain-ring-update (brain) "Keep a running tally of your neurological state." (when (and (assoc 'eSense brain) (assoc 'eegPower brain) (assoc 'poorSignalLevel brain) (> mindwave-poor-signal-level (cdr (assoc 'poorSignalLevel brain)))) (ring-insert mindwave/brain-ring brain) (when (>= (ring-length mindwave/brain-ring) mindwave/brain-ring-size) (let ((new-ring (make-ring mindwave/brain-ring-size)) (s mindwave/brain-ring-size) (collapsed-ring (reduce #'(lambda (brain total) (mindwave/brain-ring-apply '+ brain total)) (ring-elements mindwave/brain-ring) :initial-value (mindwave/make-brain-ring 0 0 0 0 0 0 0 0 0 0)))) (setq mindwave/brain-ring new-ring) (run-hook-with-args 'mindwave/brain-ring-full-hook (mindwave/brain-ring-apply 'mindwave/safe-div collapsed-ring (mindwave/make-brain-ring s s s s s s s s s s))))))) #+end_src *** Raw EEG Ring, a ring-storage of eeg values Note, I have NO idea what the timing is going to be like on this yet, and how useful it is going to be, but what the heck, why not try? The theory behind this is that you want a real fast function to store the values of the EEG waveform. This basically gives emacs the chance to buffer the waveform. Also, in my experiments, I found that there were certain times I wanted the waveform, but a lot of times I wasn't interested at all. Note, that you can just use the variable ~mindwave-eeg-ring~ directly. To change the size of the ring, you can set/customize the variable ~mindwave-eeg-ring-size~ #+name: mw-eeg-ring #+begin_src emacs-lisp :tangle yes (defcustom mindwave-eeg-ring-size 2048 "Size of the eeg ring to store. Generally the sampling frequency of a mindwave is 512hz, so to get an 8 second ring, you would want a size of 4096." :group 'mindwave :type 'int ) (defvar mindwave-eeg-ring (make-ring mindwave-eeg-ring-size) "A ring full of mindwave raw eeg values.") #+end_src ** Configure *** Ask for raw output #+begin_src emacs-lisp :tangle yes (defun mindwave-get-raw (raw) "Return raw output from mindwave. RAW is a boolean value as to whether or not to listen for raw values" (mindwave-send-string (json-encode `(("enableRawOutput" . ,(if raw t json-false)) ("format" . "Json"))))) #+end_src ** Ask for authorization Authorization doesn't seem to be supported yet... but here it is at any rate. #+begin_src emacs-lisp :tangle yes (defvar mindwave-authorized-p nil "whether or not app is authorized") #+end_src #+begin_src emacs-lisp :tangle yes (defun mindwave-authorize () "provides an autorization request to the mindwave server" (mindwave-send-string (json-encode `(("appName" . ,mindwave-appName) ("appKey" . ,mindwave-appKey))))) #+end_src #+begin_src emacs-lisp (defun mindwave-authorized-hook (out) "test" ;(message "Authorize listener: %s" out) ) #+end_src * The End #+begin_src emacs-lisp :tangle yes (provide 'mindwave-emacs) ;;; mindwave-emacs.el ends here #+end_src