Raspberry Pi Weighting Control System
This project serves as a simple weighting control system, that was realized as a Bachelor Thesis
keyboard.cpp
Go to the documentation of this file.
1 #include "keyboard.h"
2 
3 #include <memory>
4 #include <SDL2/SDL.h>
5 #include <unistd.h>
6 #include <sys/time.h>
7 #include <vector>
8 #include <stdexcept>
9 #include <map>
10 #include <chrono>
11 #include <spdlog/spdlog.h>
12 
13 #include "app_workspace.h"
14 
15 #define READ_INTERVAL 20000 // in us
16 #define RESPONSES_AVG_CNT 200 // avarage of response times is made after this count
17 
18 namespace keyboard {
19 /*********************************************************************************************************************/
20 /* Global variables */
21 /*********************************************************************************************************************/
22 
23  // hopefully it initializes like this
24  std::queue<kb_event> event_queue;
25  std::mutex eq_mutex;
26 
27  // struct timeval last_read; // changed to local chrono time_point
28  // std::mutex lr_mtx;
29  bool kb_testing = false;
30  std::mutex test_mtx;
31  unsigned long responses_sum = 0;
33 
34  std::unique_ptr<spi_config_t> spi_config;
35  std::unique_ptr<SPI> my_spi = nullptr;
36  bool capturing_events = true;
37 
38 /*********************************************************************************************************************/
39 /* Local function definitions */
40 /*********************************************************************************************************************/
41 
42  // see implementation for documentation
43  uint16_t buffer_to_keycode(const uint8_t *rx_buffer);
44  void push_event(uint16_t scancode, uint8_t flags);
45  // std::chrono::time_point<std::chrono::high_resolution_clock>* get_last_read();
46 
47 /*********************************************************************************************************************/
48 /* Implementation of header function definitions */
49 /*********************************************************************************************************************/
50 
59  if (event_queue.size() <= 0)
60  return NULL;
61 
62  std::lock_guard<std::mutex> eq_guard(eq_mutex);
63 
64  kb_event* res = &event_queue.front();
65  event_queue.pop();
66 
67  return res;
68  }
69 
71  for (size_t i = 0; i < event_queue.size(); i++) {
72  event_queue.pop();
73  }
74  }
75 
86  int init(const char* device, uint8_t mode, uint32_t speed, uint16_t delay, uint8_t bits_per_word) {
87  spi_config = std::unique_ptr<spi_config_t>(new spi_config_t());
88  spi_config->mode = mode;
89  spi_config->speed = speed;
90  spi_config->delay = delay;
91  spi_config->bits_per_word = bits_per_word;
92 
93  spdlog::info("keyboard.cpp - Initializing SPI keyboard (through parameters, enable debug to print config");
94  spdlog::debug("keyboard.cpp - SPI conf dev: {0}" , device);
95  spdlog::debug("keyboard.cpp - SPI conf mode: {0}" , spi_config->mode);
96  spdlog::debug("keyboard.cpp - SPI conf bits_p_w: {0}" , spi_config->bits_per_word);
97  spdlog::debug("keyboard.cpp - SPI conf speed: {0}" , spi_config->speed);
98  spdlog::debug("keyboard.cpp - SPI conf delay: {0}" , spi_config->delay);
99 
100  my_spi = std::unique_ptr<SPI>(new SPI(device, spi_config.get()));
101 
102  if (!my_spi->begin()) {
103  spdlog::critical("keyboard.cpp - Failed to start SPI communication for keyboard");
104  return 1;
105  }
106 
107  spdlog::info("keyboard.cpp - Successfully initialized SPI keyboard");
108  return 0;
109  }
110 
117  app_workspace_ns::spi_config *config = &(app_workspace::get_instance()->main_config->keyb_conf);
118  spdlog::info("keyboard.cpp - Initializing SPI keyboard with config (enable debug to print config)");
119  spdlog::debug("keyboard.cpp - SPI conf dev: {0}" , config->dev);
120  spdlog::debug("keyboard.cpp - SPI conf mode: {0}" , config->config->mode);
121  spdlog::debug("keyboard.cpp - SPI conf bits_p_w: {0}", config->config->bits_per_word);
122  spdlog::debug("keyboard.cpp - SPI conf speed: {0}" , config->config->speed);
123  spdlog::debug("keyboard.cpp - SPI conf delay: {0}" , config->config->delay);
124 
125  my_spi = std::unique_ptr<SPI>(new SPI(config->dev.c_str(), config->config.get()));
126 
127  if (!my_spi->begin()) {
128  spdlog::critical("keyboard.cpp - Failed to start SPI communication for keyboard");
129  return 1;
130  }
131 
132  spdlog::info("keyboard.cpp - Successfully initialized SPI keyboard");
133  return 0;
134  }
135 
141  // need 4 bytes to correctly read key code (aka scan code)
142  // 1st byte always == 00 (or ff) - chipselect, need 4th byte to correctly read the bytes inbetween
143  uint8_t rx_buffer[4] = {0};
144  uint8_t event_flags = 0;
145  std::mutex tst_mtx;
146  uint16_t prev_scancode = 0, cur_scancode = 0;
147  std::chrono::time_point<std::chrono::high_resolution_clock> last_read;
148  //struct timeval lp_time, cp_time; // lp = last_press, cp = current_press
149 
150  spdlog::info("keyboard.cpp - Starting event capturing.");
151 
152  while (capturing_events) {
153  prev_scancode = cur_scancode;
154 
155  event_flags = 0;
156  memset(rx_buffer, 0, 4);
157  my_spi->read(rx_buffer, 4);
158 
159  cur_scancode = buffer_to_keycode(rx_buffer);
160 
161  if (prev_scancode != 0x0000 || cur_scancode != 0x0000) {
162  if (cur_scancode != 0x0000)
163  event_flags |= KB_FLAGS_KEY_DOWN;
164  if (cur_scancode == prev_scancode)
165  event_flags |= KB_FLAGS_REPEATING;
166  if (get_kb_testing_inner()) {
167  std::chrono::time_point<std::chrono::high_resolution_clock> now =
168  std::chrono::high_resolution_clock::now();
169  unsigned long us_diff =
170  std::chrono::duration_cast<std::chrono::microseconds>(now - last_read).count();
171  unsigned long ms_diff = us_diff / 1000;
172  last_read = now;
173 
174  tst_mtx.lock();
175  app_workspace::get_instance()->kb_delay_us = us_diff;
176  app_workspace::get_instance()->kb_delay_ms = ms_diff;
177  tst_mtx.unlock();
178 
179  spdlog::info("event_keyboard.cpp - keyboard delay test: {0} ms ({1} us)", ms_diff, us_diff);
180 
182  spdlog::warn("event_keyboard.cpp - avarage delay over {0} samples is: {1} ms ({2} us)",
185  responses_passed = 0;
186  responses_sum = 0;
187  }
188 
190  responses_sum += us_diff;
191 
192  event_flags |= KB_FLAGS_TEST;
193  }
194 
195  if (cur_scancode == 0x0000) // if current_scancode is 0, send prev event as KEY UP
196  push_event(prev_scancode, event_flags);
197  else
198  push_event(cur_scancode, event_flags);
199  }
200 
201  usleep(READ_INTERVAL); // sleep between reads
202  }
203  }
204 
209  void clean() {
210  spdlog::info("keyboard.cpp - Cleaning SPI keyboard");
211  capturing_events = false;
212  // my_spi is unique_ptr, should destruct automatically
213  }
214 
217  std::lock_guard<std::mutex> kb_guard(test_mtx);
218  return kb_testing;
219  }
220 
222  void set_kb_testing_inner(bool val) {
223  std::lock_guard<std::mutex> kb_guard(test_mtx);
224  kb_testing = val;
225  }
226 
227 /*********************************************************************************************************************/
228 /* Implementation of local function definitions */
229 /*********************************************************************************************************************/
230 
239  uint16_t buffer_to_keycode(const uint8_t *rx_buffer) {
240  uint16_t key = 0;
241  key = (key | rx_buffer[1]) << 8;
242  key |= rx_buffer[2];
243  return key;
244  }
245 
252  void push_event(uint16_t scancode, uint8_t flags) {
253  std::lock_guard<std::mutex> eq_guard(eq_mutex);
254 
255  kb_event event;
256  event.scancode = scancode;
257  event.flags = flags;
258  gettimeofday(&event.timestamp, NULL);
259  event_queue.push(event);
260  }
261 
262  // std::chrono::time_point<std::chrono::high_resolution_clock>* get_last_read() {
263  // std::lock_guard<std::mutex> lock(lr_mtx);
264  // return &last_read;
265  // }
266 }
static std::unique_ptr< app_workspace > & get_instance()
Get the instance app_workspace which is a singleton.
#define READ_INTERVAL
Definition: keyboard.cpp:15
#define RESPONSES_AVG_CNT
Definition: keyboard.cpp:16
@ KB_FLAGS_REPEATING
Definition: keyboard.h:14
@ KB_FLAGS_TEST
Definition: keyboard.h:15
@ KB_FLAGS_KEY_DOWN
Definition: keyboard.h:13
bool kb_testing
Definition: keyboard.cpp:29
std::mutex eq_mutex
Definition: keyboard.cpp:25
void clear_event_queue()
With this the event queue can be cleared but all events are discarded. This can be used if limiting t...
Definition: keyboard.cpp:70
void push_event(uint16_t scancode, uint8_t flags)
Creates and pushes event into event_queue.
Definition: keyboard.cpp:252
uint16_t buffer_to_keycode(const uint8_t *rx_buffer)
Concats keycode from rx_buffer into one (uint16_t) variable. rx_buffer is 4 bytes (this is assumed to...
Definition: keyboard.cpp:239
void start_capturing_events()
Infinite loop that reads SPI keyboard. Runs in it's own thread!
Definition: keyboard.cpp:140
bool capturing_events
Definition: keyboard.cpp:36
void set_kb_testing_inner(bool val)
Definition: keyboard.cpp:222
int responses_passed
Definition: keyboard.cpp:32
bool get_kb_testing_inner()
Definition: keyboard.cpp:216
int init(const char *device, uint8_t mode, uint32_t speed, uint16_t delay, uint8_t bits_per_word)
Initializes spi_config and my_spi global variables and starts SPI.
Definition: keyboard.cpp:86
std::unique_ptr< SPI > my_spi
Definition: keyboard.cpp:35
std::queue< kb_event > event_queue
Definition: keyboard.cpp:24
unsigned long responses_sum
Definition: keyboard.cpp:31
void clean()
Stops event capturing and closes SPI.
Definition: keyboard.cpp:209
std::unique_ptr< spi_config_t > spi_config
Definition: keyboard.cpp:34
int init_from_conf()
Initializes my_spi with config loaded from config file into app_workspace. Starts SPI.
Definition: keyboard.cpp:116
std::mutex test_mtx
Definition: keyboard.cpp:30
kb_event * poll_event()
Polls event from queue into param event Returns 1 on success because it is inteded to return "true" o...
Definition: keyboard.cpp:58
Structure holding SPI device and SPI config which is a library structure "spi_config_t".
std::unique_ptr< spi_config_t > config
Structure of SPI keyboard event, Contains pressed scancode, flags and timestamp of event creation.
Definition: keyboard.h:23
struct timeval timestamp
Definition: keyboard.h:26
uint16_t scancode
Definition: keyboard.h:24