main.ino 8.48 KB
Newer Older
Michael Nelson's avatar
Michael Nelson committed
1
#define MODE_OFF 0
Michael Nelson's avatar
Michael Nelson committed
2
#define DEBUG_WITH_BUILTIN_LED false
3
#define PIN_OUT_BUILTIN_LED 7
Michael Nelson's avatar
Michael Nelson committed
4 5 6 7 8 9 10 11 12 13 14

// Foreground Modes
#define FG_MODE_BLINK_L 1
#define FG_MODE_BLINK_R 2

// Background modes
#define BG_MODE_HAZARDS 1
#define BG_MODE_RUNNING_LIGHTS 2
#define BG_MODE_RUNNING_W_BLINK 3
#define BG_MODE_AUXILARY_LIGHTS 4

15
// Pins 2-6 are for itmes on the handlebars.
Michael Nelson's avatar
Michael Nelson committed
16
// Ethernet coloring plan:
17 18 19 20 21 22 23 24
// Brown          - 5V Ground
// Brown striped  - 5V Speaker
// Green          - 12V Left Blinker
// Green striped  - 12V Right Blinker
// Orange         - 12V Ground
// Orange striped - 5V Horn button
// Blue           - 5V Left handlebar grip tactile switch
// Blue striped   - 5V Right handlebar grip tactile switch
25

Michael Nelson's avatar
Michael Nelson committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

// 12V
// ===
// 
// 1 - Hot 12V              Red
// 2.- Negative Blinker L   Green
// 3 - Negative Blinker R   Yellow
// 4 - Negative Horn        Brown
// 5 - Negative ____        White
// 
// 3.3V
// ====
// 
// 1 - Log Ground
// 2 - In 0
// 3 - In 1
// 4 - In 2
// 5 - Out 3
// 6 - _____

// LLC
// ===
//
// Solid green = 4
// Striped green = 5
// Solid blue = 6
// Striped blue = 7

Michael Nelson's avatar
Michael Nelson committed
54 55 56 57
// Old config notes:
// Solid blue - Left temp wires
// Solid Green - Left util
// Striped green - Right util
Michael Nelson's avatar
Michael Nelson committed
58

Michael Nelson's avatar
Michael Nelson committed
59
// Inputs
60 61 62
#define PIN_IN_LEFT_GRIP_BUTTON 0
#define PIN_IN_RIGHT_GRIP_BUTTON 1
#define PIN_IN_HORN_BUTTON 2
63
#define PIN_IN_EXTRA_BUTTON 3
Michael Nelson's avatar
Michael Nelson committed
64 65

// Outputs
66 67 68
#define PIN_OUT_BLINKER_L 4
#define PIN_OUT_BLINKER_R 5
#define PIN_OUT_HORN 6
Michael Nelson's avatar
Michael Nelson committed
69
#define PIN_OUT_EXTRA 7
Michael Nelson's avatar
Michael Nelson committed
70 71

// Config
72 73
#define BLINK_RATE 250
#define RUNNING_LIGHT_BLINK_RATE 920
74
#define MINIMUM_PRESS_TIME 50
75 76
#define HOLD_DOWN_TIME 500
#define DEBOUNCE_COOLDOWN_TIME 200
Michael Nelson's avatar
Michael Nelson committed
77 78 79 80 81

// State
int fg_mode = MODE_OFF;
int bg_mode = MODE_OFF;
int blinker_light_state;
Michael Nelson's avatar
Michael Nelson committed
82
int last_fg_mode = MODE_OFF;
83
int last_bg_mode = MODE_OFF;
84
bool hold_waiting = false;
Michael Nelson's avatar
Michael Nelson committed
85

86 87 88 89
enum scooter_ui {
  btn_grip_left,
  btn_grip_right,
  btn_grip_both,
90

91 92
  btn_horn,

93 94 95 96 97 98 99 100 101 102
  num_ui_actions
};

// Flags calculated by the program for a single frame of loop as either single push & release
// or a hold. The hold bit is flipped ONCE at the frame exactly at the end of HOLD_DOWN_TIME.
// The user can continue to hold, which increments the input in hold_time, but won't flip
// any bit either "hit" array here.
bool input_single_hit[num_ui_actions];
bool input_hold_hit[num_ui_actions];
int hold_time[num_ui_actions];
Michael Nelson's avatar
Michael Nelson committed
103 104 105

// Time accumulator variables
int cycle_time = 0;
106
int new_input_blocked = 0;
Michael Nelson's avatar
Michael Nelson committed
107

108 109 110
bool firmware_mode = false;
SYSTEM_MODE(MANUAL);

Michael Nelson's avatar
Michael Nelson committed
111 112 113
void setup()
{
  // Initialize a serial connection for reporting values to the host
114
  Serial.begin(9600);
Michael Nelson's avatar
Michael Nelson committed
115

116
  pinMode(PIN_IN_LEFT_GRIP_BUTTON, INPUT);
117
  pinMode(PIN_IN_RIGHT_GRIP_BUTTON, INPUT);
118
  pinMode(PIN_IN_HORN_BUTTON, INPUT);
119
  pinMode(PIN_IN_EXTRA_BUTTON, INPUT);
Michael Nelson's avatar
Michael Nelson committed
120 121 122

  pinMode(PIN_OUT_BLINKER_L, OUTPUT);
  pinMode(PIN_OUT_BLINKER_R, OUTPUT);
Michael Nelson's avatar
Michael Nelson committed
123 124 125 126 127 128
  pinMode(PIN_OUT_HORN, OUTPUT);
  pinMode(PIN_OUT_EXTRA, OUTPUT);

  digitalWrite(PIN_OUT_BLINKER_L, LOW);
  digitalWrite(PIN_OUT_BLINKER_R, LOW);
  digitalWrite(PIN_OUT_HORN, LOW);
Michael Nelson's avatar
Michael Nelson committed
129

130
  if (digitalRead(PIN_IN_LEFT_GRIP_BUTTON) == LOW) {
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    digitalWrite(PIN_OUT_BLINKER_L, HIGH);
    digitalWrite(PIN_OUT_BLINKER_R, HIGH);
    delay(150);

    digitalWrite(PIN_OUT_BLINKER_L, LOW);
    digitalWrite(PIN_OUT_BLINKER_R, LOW);
    delay(150);

    digitalWrite(PIN_OUT_BLINKER_L, HIGH);
    digitalWrite(PIN_OUT_BLINKER_R, HIGH);
    delay(150);

    digitalWrite(PIN_OUT_BLINKER_L, LOW);
    digitalWrite(PIN_OUT_BLINKER_R, LOW);
    delay(150);

147 148
    firmware_mode = true;
  }
Michael Nelson's avatar
Michael Nelson committed
149 150
}

151 152 153 154 155
void debounce_subsequent_input()
{
  new_input_blocked = DEBOUNCE_COOLDOWN_TIME;
}

156
void blink_frame(bool left, bool right, int tone_divisor = 2)
Michael Nelson's avatar
Michael Nelson committed
157 158 159 160 161 162 163
{
  if (cycle_time > BLINK_RATE)
  {
    cycle_time = 0;
    blinker_light_state = !blinker_light_state;
  }

164 165
  if (cycle_time == 1 && !blinker_light_state)
    blink_speaker_frame(tone_divisor);
Michael Nelson's avatar
Michael Nelson committed
166

Michael Nelson's avatar
Michael Nelson committed
167
  // Actual light
168 169
  if (left)
    digitalWrite(PIN_OUT_BLINKER_L,       blinker_light_state ? LOW : HIGH);
Michael Nelson's avatar
Michael Nelson committed
170

171 172
  if (right)
    digitalWrite(PIN_OUT_BLINKER_R,        blinker_light_state ? LOW : HIGH);
Michael Nelson's avatar
Michael Nelson committed
173 174

  #if DEBUG_WITH_BUILTIN_LED
175
  digitalWrite(PIN_OUT_BUILTIN_LED, blinker_light_state ? LOW : HIGH);
Michael Nelson's avatar
Michael Nelson committed
176 177 178 179 180
  #endif

  cycle_time++;
}

181 182
void running_light_frame(bool with_blink = false)
{
183
  if (cycle_time >= RUNNING_LIGHT_BLINK_RATE)
184 185
    cycle_time = 0;

186 187
  digitalWrite(PIN_OUT_BLINKER_L,       !(cycle_time % 20) || (with_blink && cycle_time < 70) ? HIGH : LOW);
  digitalWrite(PIN_OUT_BLINKER_R,       !(cycle_time % 20) || (with_blink && cycle_time < 70) ? HIGH : LOW);
188

189
  #if DEBUG_WITH_BUILTIN_LED
190
  digitalWrite(PIN_OUT_BUILTIN_LED, !(cycle_time % 20) || (with_blink && cycle_time < 70) ? HIGH : LOW);
191
  #endif
192 193 194 195

  cycle_time++;
}

196 197 198 199 200
void auxiliary_light_frame()
{
  digitalWrite(PIN_OUT_BLINKER_L, HIGH);
  digitalWrite(PIN_OUT_BLINKER_R, HIGH);

201
  #if DEBUG_WITH_BUILTIN_LED
202
  digitalWrite(PIN_OUT_BUILTIN_LED, HIGH);
203
  #endif
204 205
}

206
void blink_speaker_frame(int divisor)
Michael Nelson's avatar
Michael Nelson committed
207
{
208 209 210
  /* if (!blinker_light_state && cycle_time < 40) { */
  /*   digitalWrite(PIN_OUT_SPEAKER, !(cycle_time % divisor) ? HIGH : LOW); */
  /* } */
211
  /* tone(PIN_OUT_SPEAKER, 2000, 20); */
212 213 214 215 216

  // Horn method
  /* digitalWrite(PIN_OUT_HORN, HIGH); */
  /* delay(1); */
  /* digitalWrite(PIN_OUT_HORN, LOW); */
Michael Nelson's avatar
Michael Nelson committed
217 218
}

219
// Each frame, this increments L/R/B hold time variables while the user is holding a button (or both) down.
220 221
// It also checks for the release. In that case it flips on either input_single_* or input_hold_* variables
// to be consumed later in the frame.
Michael Nelson's avatar
Michael Nelson committed
222
void read_buttons()
Michael Nelson's avatar
Michael Nelson committed
223
{
224 225 226 227
  for (int i = 0; i < num_ui_actions; i++) {
    input_single_hit[i] = false;
    input_hold_hit[i] = false;
  }
Michael Nelson's avatar
Michael Nelson committed
228

229
  if (digitalRead(PIN_IN_LEFT_GRIP_BUTTON) == LOW && digitalRead(PIN_IN_RIGHT_GRIP_BUTTON) == LOW)
230
    hold_time[btn_grip_both]++;
231
  else if (digitalRead(PIN_IN_LEFT_GRIP_BUTTON) == LOW)
232 233 234
    hold_time[btn_grip_left]++;
  else if (digitalRead(PIN_IN_RIGHT_GRIP_BUTTON) == LOW)
    hold_time[btn_grip_right]++;
235 236
  else if (digitalRead(PIN_IN_HORN_BUTTON) == LOW)
    hold_time[btn_horn]++;
Michael Nelson's avatar
Michael Nelson committed
237 238
  else
  {
239
    for (int i = 0; i < num_ui_actions; i++)
240
    {
241
      if (hold_time[i] > MINIMUM_PRESS_TIME && !new_input_blocked && !hold_waiting)
242 243 244 245
      {
        input_single_hit[i] = true;
        debounce_subsequent_input();
      }
246
    }
247

248 249
    for (int i = 0; i < num_ui_actions; i++)
      hold_time[i] = 0;
250 251 252 253

    if (hold_waiting)
      debounce_subsequent_input();

254 255 256
    hold_waiting = false;
  }

257
  for (int i = 0; i < num_ui_actions; i++)
258
  {
259 260 261 262 263 264
    if (hold_time[i] == HOLD_DOWN_TIME && !new_input_blocked)
    {
      input_hold_hit[i] = true;
      hold_waiting = true;
      debounce_subsequent_input();
    }
Michael Nelson's avatar
Michael Nelson committed
265 266 267 268 269
  }
}

void set_up_mode()
{
Michael Nelson's avatar
Michael Nelson committed
270
  read_buttons();
Michael Nelson's avatar
Michael Nelson committed
271

272
  if (input_hold_hit[btn_grip_both])
Michael Nelson's avatar
Michael Nelson committed
273
  {
274
    if (!bg_mode)
275 276
      bg_mode = BG_MODE_HAZARDS;
    else if (bg_mode >= BG_MODE_AUXILARY_LIGHTS)
277 278 279
      bg_mode = MODE_OFF;
    else
      bg_mode++;
Michael Nelson's avatar
Michael Nelson committed
280 281 282

    fg_mode = MODE_OFF;
  }
283
  else if (input_single_hit[btn_grip_left])
Michael Nelson's avatar
Michael Nelson committed
284
    fg_mode = fg_mode == FG_MODE_BLINK_L ? MODE_OFF : FG_MODE_BLINK_L;
285
  else if (input_single_hit[btn_grip_right])
286
    fg_mode = fg_mode == FG_MODE_BLINK_R ? MODE_OFF : FG_MODE_BLINK_R;
Michael Nelson's avatar
Michael Nelson committed
287 288
}

Michael Nelson's avatar
Michael Nelson committed
289 290 291
void reset_all()
{
  cycle_time = 0;
292
  blinker_light_state = 0;
Michael Nelson's avatar
Michael Nelson committed
293 294

  #if DEBUG_WITH_BUILTIN_LED
295
  digitalWrite(PIN_OUT_BUILTIN_LED, LOW);
Michael Nelson's avatar
Michael Nelson committed
296 297 298 299
  #endif

  digitalWrite(PIN_OUT_BLINKER_L, LOW);
  digitalWrite(PIN_OUT_BLINKER_R, LOW);
300
  digitalWrite(PIN_OUT_HORN, LOW);
Michael Nelson's avatar
Michael Nelson committed
301
  digitalWrite(PIN_OUT_EXTRA, LOW);
Michael Nelson's avatar
Michael Nelson committed
302 303
}

Michael Nelson's avatar
Michael Nelson committed
304 305
void loop()
{
306 307 308 309 310 311 312 313 314 315 316
  if (firmware_mode)
  {
    if (!Particle.connected())
      Particle.connect();

    Particle.process();
    delay(500);

    return;
  }

317
  delay(1);
Michael Nelson's avatar
Michael Nelson committed
318
  set_up_mode();
Michael Nelson's avatar
Michael Nelson committed
319

320
  // Reset all lights going directly from one direction to the next.
321
  if (fg_mode != last_fg_mode || bg_mode != last_bg_mode)
Michael Nelson's avatar
Michael Nelson committed
322 323
    reset_all();

324
  if (fg_mode == FG_MODE_BLINK_L || hold_time[btn_grip_left] > MINIMUM_PRESS_TIME)
325
    blink_frame(true, false);
326
  else if (fg_mode == FG_MODE_BLINK_R || hold_time[btn_grip_right] > MINIMUM_PRESS_TIME)
327 328 329
    blink_frame(false, true);
  else if (bg_mode == BG_MODE_HAZARDS)
    blink_frame(true, true, 3);
330 331 332 333
  else if (bg_mode == BG_MODE_RUNNING_LIGHTS)
    running_light_frame();
  else if (bg_mode == BG_MODE_RUNNING_W_BLINK)
    running_light_frame(true);
334 335
  else if (bg_mode == BG_MODE_AUXILARY_LIGHTS)
    auxiliary_light_frame();
Michael Nelson's avatar
Michael Nelson committed
336
  else
Michael Nelson's avatar
Michael Nelson committed
337
    reset_all();
Michael Nelson's avatar
Michael Nelson committed
338

339
  // Horn is driven asynchronously from the rest of the blinking logic
340
  if (hold_time[btn_horn] > MINIMUM_PRESS_TIME)
Michael Nelson's avatar
Michael Nelson committed
341
  {
342
    digitalWrite(PIN_OUT_HORN, HIGH); // TOOOOOOT
343 344

    #if DEBUG_WITH_BUILTIN_LED
Michael Nelson's avatar
Michael Nelson committed
345
    digitalWrite(PIN_OUT_BUILTIN_LED, HIGH);
346
    #endif
Michael Nelson's avatar
Michael Nelson committed
347
  }
348 349 350
  else
    digitalWrite(PIN_OUT_HORN, LOW);

Michael Nelson's avatar
Michael Nelson committed
351
  last_fg_mode = fg_mode;
352
  last_bg_mode = bg_mode;
353 354 355

  if (new_input_blocked > 0)
    new_input_blocked--;
Michael Nelson's avatar
Michael Nelson committed
356
}