Commit ace67795 authored by Robey Pointer's avatar Robey Pointer
Browse files

factor out the battery probe

parent f259912c
......@@ -44,7 +44,6 @@
#include "ux.h"
#include "Keyboard.h"
#define KBD_FW_REV "R1 20210815"
//#define KBD_VARIANT_STANDALONE
#define KBD_VARIANT_QWERTY_US
//#define KBD_VARIANT_NEO2
......@@ -114,52 +113,6 @@ void empty_serial(void) {
int term_x = 0;
int term_y = 0;
char response[64];
int remote_receive_string(int print) {
char done = 0;
int32_t clock = 0;
int res_x = 0;
response[0] = 0;
while (!done) {
int16_t chr = -1;
clock = 0;
while (chr==-1 || chr==0) {
chr=Serial_ReceiveByte();
clock++;
if (clock>500000) goto timeout;
}
int poke_chr = chr;
if (chr=='\n') poke_chr=' ';
if (chr!='\r') {
if (print) {
gfx_poke(term_x,term_y,poke_chr);
gfx_poke(term_x+1,term_y,' ');
term_x++;
if (term_x>=20) {
term_x=0;
term_y++;
if (term_y>=3) {
term_y=0;
}
}
}
if (res_x<63) {
response[res_x++] = chr;
response[res_x] = 0;
}
}
if (chr=='\r') done = 1;
}
timeout:
if (!done && print) gfx_poke(20,0,'T');
empty_serial();
if (print) {
gfx_flush();
}
return done;
}
void anim_hello(void) {
gfx_clear();
......@@ -197,79 +150,23 @@ void anim_goodbye(void) {
gfx_off();
}
float voltages[8];
void insert_bat_icon(char* str, int x, float v) {
char icon = 0;
if (v>=3.3) {
icon = 8;
} else if (v>=3.1) {
icon = 6;
} else if (v>=3.0) {
icon = 4;
} else if (v>=2.9) {
icon = 2;
} else {
icon = 0;
}
str[x] = 4*32+icon;
str[x+1] = 4*32+icon+1;
}
int low_battery_alert = 0;
void remote_check_for_low_battery(void) {
char bat_gauge[5] = {0,0,0,0,0};
low_battery_alert = 0;
empty_serial();
static void remote_check_for_low_battery(void) {
battery_info_t info;
controller_probe_batteries(&info);
Serial_SendByte('c');
Serial_SendByte('\r');
Delay_MS(1);
remote_receive_string(0);
for (int i=0; i<8; i++) {
voltages[i] = ((float)((response[i*3]-'0')*10 + (response[i*3+1]-'0')))/10.0;
if (voltages[i]<0) voltages[i]=0;
if (voltages[i]>=10) voltages[i]=9.9;
if (voltages[i]<3.0) {
for (int i = 0; i < 8; i++) {
if (info.decivolts[i] < 30) {
low_battery_alert = 1;
}
}
int gauge_offset = 3*8+2+5+2+5+1;
strncpy(bat_gauge, &response[gauge_offset], 3);
if (bat_gauge[0] == '?') {
// battery charge level unknown
} else {
int percent = atoi(bat_gauge);
if (percent<10) {
low_battery_alert = 1;
}
if (info.total_percent > 0 && info.total_percent < 10) {
low_battery_alert = 1;
}
}
void remote_get_status(void) {
gfx_clear();
empty_serial();
gfx_poke_str(0, 2, "MNT Reform Keyboard");
gfx_poke_str(0, 3, KBD_FW_REV);
gfx_on();
gfx_flush();
#ifndef KBD_VARIANT_STANDALONE
term_x = 0;
term_y = 0;
Serial_SendByte('s');
Serial_SendByte('\r');
Delay_MS(1);
remote_receive_string(1);
#endif
}
int oledbrt=0;
void oled_brightness_inc(void) {
oledbrt+=10;
......@@ -496,7 +393,7 @@ int execute_meta_function(int keycode) {
return 0;
}
else if (keycode == KEY_S) {
remote_get_status();
ux_show_status(&display);
return 0;
}
else if (keycode == KEY_F1) {
......@@ -860,93 +757,3 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn
*ReportSize = sizeof(USB_KeyboardReport_Data_t);
return false;
}
#if 0
#include "text_display.h"
/** HID class driver callback function for the processing of HID reports from the host.
*
* \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced
* \param[in] ReportID Report ID of the received report from the host
* \param[in] ReportType The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature
* \param[in] ReportData Pointer to a buffer where the received report has been stored
* \param[in] ReportSize Size in bytes of the received HID report
*/
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
const uint8_t ReportID,
const uint8_t ReportType,
const void* ReportData,
const uint16_t ReportSize)
{
uint8_t* data = (uint8_t*)ReportData;
if (ReportSize<4) return;
if (memcmp(data, "SIZE", 4) == 0) {
gfx_on();
gfx_poke(0, 0, '0' + (ReportSize / 1000));
gfx_poke(1, 0, '0' + ((ReportSize / 100) % 10));
gfx_poke(2, 0, '0' + ((ReportSize / 10) % 10));
gfx_poke(3, 0, '0' + ((ReportSize) % 10));
gfx_flush();
}
if (memcmp(data, "test", 4) == 0) {
text_display_t display;
extern monospace_font_t font_oranj;
text_display_init(&display, &font_oranj);
text_display_write(&display, "This is a test of the\noranj font on a tiny\nOLED.\n");
text_display_putc(&display, 0);
text_display_putc(&display, 6);
text_display_putc(&display, ' ');
text_display_putc(&display, 5);
text_display_putc(&display, 7);
text_display_flush(&display);
}
if (data[0]=='O' && data[1]=='L' && data[2]=='E' && data[3]=='D') {
// OLED: write characters on display
gfx_on();
for (int y=0; y<4; y++) {
for (int x=0; x<21; x++) {
gfx_poke(x,y,data[4+y*21+x]);
}
}
gfx_flush();
}
if (data[0]=='O' && data[1]=='I' && data[2]=='N' && data[3]=='V') {
gfx_clear_invert();
gfx_invert_row(data[4]-'0');
}
else if (data[0]=='L' && data[1]=='I' && data[2]=='T' && data[3]=='E') {
char brite = data[4]-'0';
brite++;
if (brite<=1) brite=0;
if (brite>9) brite=9;
kbd_brightness_set(brite);
}
else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='0') {
// PWR0: shutdown (turn off power rails)
remote_turn_off_som();
}
else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='3') {
// PWR3: aux power off
remote_turn_off_aux();
}
else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='4') {
// PWR4: aux power on
remote_turn_on_aux();
}
else if (data[0]=='U' && data[1]=='A' && data[2]=='R' && data[3]=='1') {
// UAR1: UART reporting on
remote_enable_som_uart();
}
else if (data[0]=='U' && data[1]=='A' && data[2]=='R' && data[3]=='0') {
// UAR0: UART reporting off
remote_disable_som_uart();
}
else if (data[0]=='R' && data[1]=='P' && data[2]=='R' && data[3]=='T') {
// RPRT: Report power stats to UART
remote_report_voltages();
}
}
#endif
......@@ -33,58 +33,56 @@
* Header file for Keyboard.c.
*/
#ifndef _KEYBOARD_H_
#define _KEYBOARD_H_
/* Includes: */
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <string.h>
#include "Descriptors.h"
#include <LUFA/Drivers/Board/Joystick.h>
#include <LUFA/Drivers/Board/LEDs.h>
#include <LUFA/Drivers/Board/Buttons.h>
#include <LUFA/Drivers/USB/USB.h>
#include <LUFA/Platform/Platform.h>
/* Macros: */
/** LED mask for the library LED driver, to indicate that the USB interface is not ready. */
#define LEDMASK_USB_NOTREADY LEDS_LED1
/** LED mask for the library LED driver, to indicate that the USB interface is enumerating. */
#define LEDMASK_USB_ENUMERATING (LEDS_LED2 | LEDS_LED3)
/** LED mask for the library LED driver, to indicate that the USB interface is ready. */
#define LEDMASK_USB_READY (LEDS_LED2 | LEDS_LED4)
/** LED mask for the library LED driver, to indicate that an error has occurred in the USB interface. */
#define LEDMASK_USB_ERROR (LEDS_LED1 | LEDS_LED3)
/* Function Prototypes: */
void SetupHardware(void);
void EnterPowerOff(void);
void EVENT_USB_Device_Connect(void);
void EVENT_USB_Device_Disconnect(void);
void EVENT_USB_Device_ConfigurationChanged(void);
void EVENT_USB_Device_ControlRequest(void);
void EVENT_USB_Device_StartOfFrame(void);
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
uint8_t* const ReportID,
const uint8_t ReportType,
void* ReportData,
uint16_t* const ReportSize);
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
const uint8_t ReportID,
const uint8_t ReportType,
const void* ReportData,
const uint16_t ReportSize);
#endif
#pragma once
#define KBD_FW_REV "R1 20210815"
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <string.h>
#include "Descriptors.h"
#include <LUFA/Drivers/Board/Joystick.h>
#include <LUFA/Drivers/Board/LEDs.h>
#include <LUFA/Drivers/Board/Buttons.h>
#include <LUFA/Drivers/USB/USB.h>
#include <LUFA/Platform/Platform.h>
/* Macros: */
/** LED mask for the library LED driver, to indicate that the USB interface is not ready. */
#define LEDMASK_USB_NOTREADY LEDS_LED1
/** LED mask for the library LED driver, to indicate that the USB interface is enumerating. */
#define LEDMASK_USB_ENUMERATING (LEDS_LED2 | LEDS_LED3)
/** LED mask for the library LED driver, to indicate that the USB interface is ready. */
#define LEDMASK_USB_READY (LEDS_LED2 | LEDS_LED4)
/** LED mask for the library LED driver, to indicate that an error has occurred in the USB interface. */
#define LEDMASK_USB_ERROR (LEDS_LED1 | LEDS_LED3)
/* Function Prototypes: */
void SetupHardware(void);
void EVENT_USB_Device_Connect(void);
void EVENT_USB_Device_Disconnect(void);
void EVENT_USB_Device_ConfigurationChanged(void);
void EVENT_USB_Device_ControlRequest(void);
void EVENT_USB_Device_StartOfFrame(void);
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
uint8_t* const ReportID,
const uint8_t ReportType,
void* ReportData,
uint16_t* const ReportSize);
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
const uint8_t ReportID,
const uint8_t ReportType,
const void* ReportData,
const uint16_t ReportSize);
void EnterPowerOff(void);
......@@ -9,6 +9,7 @@
// controller, which has things like battery status.
#include <stdbool.h>
#include <stdlib.h>
#include "LUFA/Drivers/Peripheral/Serial.h"
#include "controller.h"
......@@ -60,3 +61,32 @@ const char *controller_request(const char *command) {
return NULL;
}
}
void controller_probe_batteries(battery_info_t *info) {
memset(info->decivolts, 0, 8);
info->total_amps = info->total_volts = 0;
info->total_percent = 0;
const char *response = controller_request("c");
// lpc format: 32 32 32 32 32 32 32 32 mA 0256mV26143 ???%
// | | | | | | | | | | | |
// 0 3 6 9 12 15 18 21 24| | |
// 26 33 39
// |
// `- can be a minus
#define amps_offset (3 * 8 + 2)
#define volts_offset (amps_offset + 5 + 2)
#define battery_offset (volts_offset + 6)
for (int i = 0; i < 8; i++) {
info->decivolts[i] = (response[i * 3] - '0') * 10 + response[i * 3 + 1] - '0';
if (info->decivolts[i] > 99) info->decivolts[i] = 0; // garbage data.
}
const char *bat_gauge = &response[battery_offset];
while (*bat_gauge == ' ') bat_gauge++;
info->total_amps = ((float)atoi(&response[amps_offset])) / 1000.0;
info->total_volts = ((float)atoi(&response[volts_offset])) / 1000.0;
info->total_percent = atoi(bat_gauge);
}
......@@ -7,5 +7,14 @@
#pragma once
// battery info reported by the controller, parsed:
typedef struct {
uint8_t decivolts[8]; // volts of each battery (times 10)
float total_amps;
float total_volts;
uint8_t total_percent; // 0 - 100
} battery_info_t;
void controller_init(void);
const char *controller_request(const char *command);
void controller_probe_batteries(battery_info_t *info);
......@@ -11,73 +11,78 @@
#include "text_display.h"
#include "oranj_reform.h"
const monospace_font_t font_oranj = { oranj_reform_font_data, 8, 6 };
const monospace_font_t font_oranj = {oranj_reform_font_data, 8, 6};
void text_display_init(text_display_t *display, const monospace_font_t *font) {
display->font = font;
display->height = DisplayHeight / display->font->height;
display->width = DisplayWidth / display->font->width;
text_display_clear(display);
display->font = font;
display->height = DisplayHeight / display->font->height;
display->width = DisplayWidth / display->font->width;
text_display_clear(display);
}
void text_display_clear(text_display_t *display) {
memset(display->cells, ' ', sizeof(display->cells));
memset(display->invert, 0, sizeof(display->invert));
display->cursor = 0;
display->dirty = true;
memset(display->cells, ' ', sizeof(display->cells));
memset(display->invert, 0, sizeof(display->invert));
display->cursor = 0;
display->dirty = true;
}
// scroll up one line.
void text_display_scroll(text_display_t *display) {
memmove(display->cells, display->cells + display->width, (display->height - 1) * display->width);
memset(display->cells + (display->height - 1) * display->width, ' ', display->width);
display->dirty = true;
memmove(display->cells, display->cells + display->width, (display->height - 1) * display->width);
memset(display->cells + (display->height - 1) * display->width, ' ', display->width);
display->dirty = true;
}
void text_display_putc(text_display_t *display, uint8_t c) {
if (display->cursor >= display->width * display->height) {
display->cursor -= display->width;
text_display_scroll(display);
}
display->cells[display->cursor] = c;
display->cursor++;
display->dirty = true;
while (display->cursor >= display->width * display->height) {
display->cursor -= display->width;
text_display_scroll(display);
}
display->cells[display->cursor] = c;
display->cursor++;
display->dirty = true;
}
void text_display_write(text_display_t *display, const char *text) {
while (*text) {
if (*text == '\n') {
display->cursor = ((display->cursor / display->width) + 1) * display->width;
} else {
text_display_putc(display, *text);
while (*text) {
if (*text == '\n') {
display->cursor = ((display->cursor / display->width) + 1) * display->width;
} else {
text_display_putc(display, *text);
}
text++;
}
text++;
}
}
void text_display_move(text_display_t *display, uint8_t x, uint8_t y) {
display->cursor = display->width * y + x;
if (display->cursor >= display->width * display->height) display->cursor = 0;
}
void text_display_flush(text_display_t *display) {
if (!display->dirty) return;
uint8_t x_offset = (DisplayWidth - display->width * display->font->width) / 2;
if (!display->dirty) return;
uint8_t x_offset = (DisplayWidth - display->width * display->font->width) / 2;
oled_on();
oled_home();
oled_start_render();
oled_on();
oled_home();
oled_start_render();
uint8_t cursor = 0;
for (uint8_t y = 0; y < display->height; y++) {
for (uint8_t pad = 0; pad < x_offset; pad++) oled_render(0);
for (uint8_t x = 0; x < display->width; x++) {
const uint8_t *glyph = display->font->glyphs + (display->cells[cursor] & 0x7f) * display->font->width;
for (uint8_t column = 0; column < display->font->width; column++) {
uint8_t data = pgm_read_byte(glyph + column);
if (display->invert[cursor >> 3] & (1 << (cursor & 7))) data = ~data;
oled_render(data);
}
cursor++;
uint8_t cursor = 0;
for (uint8_t y = 0; y < display->height; y++) {
for (uint8_t pad = 0; pad < x_offset; pad++) oled_render(0);
for (uint8_t x = 0; x < display->width; x++) {
const uint8_t *glyph = display->font->glyphs + (display->cells[cursor] & 0x7f) * display->font->width;
for (uint8_t column = 0; column < display->font->width; column++) {
uint8_t data = pgm_read_byte(glyph + column);
if (display->invert[cursor >> 3] & (1 << (cursor & 7))) data = ~data;
oled_render(data);
}
cursor++;
}
for (uint8_t pad = 0; pad < x_offset; pad++) oled_render(0);
}
for (uint8_t pad = 0; pad < x_offset; pad++) oled_render(0);
}
oled_finish_render();
display->dirty = false;
oled_finish_render();
display->dirty = false;
}
......@@ -42,4 +42,5 @@ void text_display_clear(text_display_t *display);
void text_display_scroll(text_display_t *display);
void text_display_putc(text_display_t *display, uint8_t c);
void text_display_write(text_display_t *display, const char *text);
void text_display_move(text_display_t *display, uint8_t x, uint8_t y);
void text_display_flush(text_display_t *display);
......@@ -8,6 +8,7 @@
#include <stdio.h>
#include <stdlib.h>
#include "controller.h"
#include "Keyboard.h"
#include "text_display.h"
#include "ux.h"
......@@ -38,38 +39,38 @@ static const char *show_one_battery(uint8_t decivolts) {
}
void ux_show_battery(text_display_t *display) {
const char *response = controller_request("c");
// lpc format: 32 32 32 32 32 32 32 32 mA 0256mV26143 ???%
// | | | | | | | | | | | |
// 0 3 6 9 12 15 18 21 24| | |
// 26 33 39
// |
// `- can be a minus
#define amps_offset (3 * 8 + 2)
#define volts_offset (amps_offset + 5 + 2)
#define battery_offset (volts_offset + 6)
uint8_t decivolts[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < 8; i++) {
decivolts[i] = (response[i * 3] - '0') * 10 + response[i * 3 + 1] - '0';
if (decivolts[i] > 99) decivolts[i] = 0; // garbage data.
}
const char *bat_gauge = &response[battery_offset];
float bat_amps = ((float)atoi(&response[amps_offset])) / 1000.0;
float bat_volts = ((float)atoi(&response[volts_offset])) / 1000.0;
battery_info_t info;
controller_probe_batteries(&info);
// plot, using oranj (6x8) so all the info will fit
text_display_init(display, &font_oranj);
char str[32];
sprintf(str, "%s %s %.4s\n", show_one_battery(decivolts[0]), show_one_battery(decivolts[4]), bat_gauge);
sprintf(str, "%s %s %3d%%",
show_one_battery(info.decivolts[0]), show_one_battery(info.decivolts[4]), info.total_percent);
text_display_write(display, str);
sprintf(str, "%s %s \n", show_one_battery(decivolts[1]), show_one_battery(decivolts[5]));
sprintf(str, "%s %s ",
show_one_battery(info.decivolts[1]), show_one_battery(info.decivolts[5]));
text_display_write(display, str);
sprintf(str, "%s %s %2.2fA\n", show_one_battery(decivolts[2]), show_one_battery(decivolts[6]), bat_amps);
sprintf(str, "%s %s %5.2fA",
show_one_battery(info.decivolts[2]), show_one_battery(info.decivolts[6]), info.total_amps);
text_display_write(display, str);
sprintf(str, "%s %s %2.2fV\n", show_one_battery(decivolts[3]), show_one_battery(decivolts[7]), bat_volts);
sprintf(str, "%s %s %5.2fV",
show_one_battery(info.decivolts[3]), show_one_battery(info.decivolts[7]), info.total_volts);
text_display_write(display, str);
text_display_flush(display);
}