Select Git revision
conanfile.py
main.c 12.38 KiB
/* * Copyright (c) 2023 Carl Klemm <carl@uvos.xyz>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
* * Neither the name of %ORGANIZATION% nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include "coincellhell/coincellhell.h"
#include "options.h"
#include "float.h"
static void print_commands(void)
{
puts("Valid commands:");
puts("set [HEATER] [TEMP]\t | set the temperature setpoint of the given heater");
puts("ramp [HEATER] [TEMP] [SECONDS] | ramp to the given temperature in given seconds");
puts("get [HEATER] [LOCATION]\t | get the temperature of the given heater");
puts("get_setpoint [HEATER]\t | get the temperature setpoint of the given heater");
puts("state\t\t\t | get the state of eatch heater");
puts("ready\t\t\t | check the ready state of all heaters");
puts("enable\t\t\t | enable the heaters");
puts("disable\t\t\t | disable the heaters");
puts("read [ADDRESS] [LENGTH]\t | read from the device eeprom at address");
puts("write [ADDRESS] [LENGTH] | write to the device eeprom at address");
puts("git\t\t\t | get the git revision of the firmware on the device");
puts("watchdog\t\t\t | enables the on device command watchdog");
puts("recal [BOOLEAN]\t\t | enables or disables periodic recal");
}
static int convert_string_to_heater_id(const char* str)
{
char* str_end;
long id = strtol(str, &str_end, 10);
if(str == str_end || id < 0 || id > 3)
{
puts("HEATER must be a whole nummber between 0 and 3");
return -1;
}
return id;
}
static long convert_string_to_seconds(const char* str)
{
char* str_end;
long seconds = strtol(str, &str_end, 10);
if(str == str_end || seconds < 0)
{
puts("SECONDS must be a whole nummber above 0");
return -1;
}
return seconds;
}
static float convert_string_to_temperature(const char* str)
{
char* str_end;
float temperature = strtof(str, &str_end);
if(str == str_end || temperature < 0 || temperature > 100)
{
puts("TEMPERATURE must be a float between 0 and 100");
return FLT_MIN;
}
return temperature;
}
static temperature_sensor_location_t convert_string_to_location(const char* str)
{
if(strcmp(str, "both") == 0)
return TEMP_LOCATION_BOTH;
if(strcmp(str, "front") == 0)
return TEMP_LOCATION_FRONT;
if(strcmp(str, "side") == 0)
return TEMP_LOCATION_SIDE;
return TEMP_LOCATION_INVALID;
}
static int process_commands(char** commands, size_t command_count, struct coincellhell* hell, char* name)
{
int ret = 0;
if(strcmp(commands[0], "help") == 0)
{
print_commands();
}
else if(strcmp(commands[0], "set") == 0)
{
if(command_count < 3)
{
printf("Usage %s %s [HEATER] [TEMPERATURE]\n", name, commands[0]);
return 1;
}
int id = convert_string_to_heater_id(commands[1]);
if(id < 0)
return 1;
float temperature = convert_string_to_temperature(commands[2]);
if(temperature == FLT_MIN)
return 2;
ret = coincellhell_set_temperature(hell, id, temperature);
}
else if(strcmp(commands[0], "ramp") == 0)
{
if(command_count < 4)
{
printf("Usage %s %s [HEATER] [TEMPERATURE] [SECONDS]\n", name, commands[0]);
return 1;
}
int id = convert_string_to_heater_id(commands[1]);
if(id < 0)
return 1;
float temperature = convert_string_to_temperature(commands[2]);
if(temperature == FLT_MIN)
return 2;
long seconds = convert_string_to_seconds(commands[3]);
if(seconds < 0)
return 2;
time_t endtime = time(NULL) + seconds;
ret = coincellhell_set_temperature_ramp(hell, id, endtime, temperature);
}
else if(strcmp(commands[0], "get") == 0)
{
if(command_count == 1)
{
for(uint8_t i = 0; i < 4; ++i)
{
float front = 0;
float side = 0;
int err = 0;
err |= coincellhell_get_temperature(hell, i, TEMP_LOCATION_FRONT, &front);
err |= coincellhell_get_temperature(hell, i, TEMP_LOCATION_SIDE, &side);
if(!err)
printf("Heater %i: front temperature %f, side temperature %f, avg temperature %f\n", i, front, side, (front+side)/2);
else
printf("Heater %i: UNABLE TO READ\n", i);
}
}
else if(command_count < 3)
{
printf("Usage %s %s [HEATER] [LOCATION]\n", name, commands[0]);
ret = 1;
}
else
{
int id = convert_string_to_heater_id(commands[1]);
if(id < 0)
return 1;
temperature_sensor_location_t location = convert_string_to_location(commands[2]);
if(location == TEMP_LOCATION_INVALID)
return 2;
float temperature;
ret = coincellhell_get_temperature(hell, id, location, &temperature);
if(ret == 0)
printf("%f\n", temperature);
}
}
else if(strcmp(commands[0], "get_setpoint") == 0)
{
if(command_count == 1)
{
for(uint8_t i = 0; i < 4; ++i)
{
float setpoint = 0;
int err = coincellhell_get_temperature_setpoint(hell, i, &setpoint);
if(!err)
printf("Heater %i: setpoint %f\n", i, setpoint);
else
printf("Heater %i: UNABLE TO READ\n", i);
}
}
else
{
int id = convert_string_to_heater_id(commands[1]);
if(id < 0)
return 1;
float temperature;
ret = coincellhell_get_temperature_setpoint(hell, id, &temperature);
if(ret == 0)
printf("%f\n", temperature);
}
}
else if(strcmp(commands[0], "state") == 0)
{
for(uint8_t i = 0; i < 4; ++i)
{
struct heater_state state;
ret = coincellhell_get_state(hell, i, &state);
if(ret < 0)
{
puts("could not read");
break;
}
printf("Heater %d:\n\tEnabled: %s\n\tReady: %s\n\tRamp: %s\n\tFault: %s\n\tSet point: %f\n\tDAC command: %d\n",
i, state.enabled ? "True" : "False", state.ready ? "True" : "False", state.ramp ? "True" : "False", state.fault ? "True" : "False",
state.setpoint, state.dacCommand);
float temperature;
ret = coincellhell_get_temperature(hell, i, TEMP_LOCATION_BOTH, &temperature);
printf("\tTemperature: %f\n", temperature);
if(state.ramp)
{
printf("\tRamp Target: %f\n\tRamp Start Time: %lld\n\tRamp Stop Time: %lld\n",
state.rampTarget, (long long)state.rampStartTime, (long long)state.rampStopTime);
}
if(state.fault)
printf("\tFault: %s\n", coincellhell_string_for_fault(state.faultType));
}
}
else if(strcmp(commands[0], "enable") == 0)
{
for(uint8_t i = 0; i < 4; ++i)
{
ret = coincellhell_set_enabled(hell, i, true);
if(ret < 0)
{
puts("could not read");
break;
}
}
}
else if(strcmp(commands[0], "disable") == 0)
{
for(uint8_t i = 0; i < 4; ++i)
{
ret = coincellhell_set_enabled(hell, i, false);
if(ret < 0)
{
puts("could not read");
break;
}
}
}
else if(strcmp(commands[0], "ready") == 0)
{
bool ready = false;
ret = coincellhell_check_ready(hell, &ready);
if(ret == 0)
printf("%s\n", ready ? "true" : "false");
else
puts("could not read");
}
else if(strcmp(commands[0], "write") == 0)
{
if(command_count < 3)
{
printf("Usage %s %s [ADDRESS] [VALUE]\n", name, commands[0]);
return 2;
}
long address = strtol(commands[1], NULL, 10);
long value = strtol(commands[2], NULL, 10);
if(address > UINT16_MAX || address < 0 || value < 0 || value > UINT16_MAX)
{
puts("Value or address is out of range");
return 2;
}
if(address % 2 == 1)
{
puts("Address must be a even number, 16 bits are written at a time");
return 2;
}
coincellhell_write_eeprom(hell, address, value);
}
else if(strcmp(commands[0], "read") == 0)
{
if(command_count < 2)
{
printf("Usage %s %s [ADDRESS] [LENGTH]\n", name, commands[0]);
return 2;
}
long address = strtol(commands[1], NULL, 10);
long length = 2;
if(command_count > 2)
length = strtol(commands[2], NULL, 10);
if(address > UINT16_MAX || address < 0 || length < 0 || length > UINT16_MAX)
{
puts("Value or address is out of range");
return 2;
}
if(address % 2 == 1)
{
puts("Address must be a even number, 16 bits are read at a time");
return 2;
}
if(length % 2 == 1)
{
puts("Length must be a even number, 16 bits are read at a time");
return 2;
}
for(uint16_t i = address; i - address < length; i+=2)
{
uint16_t value = coincellhell_read_eeprom(hell, i);
printf("%u: %u\n", i, value);
}
}
else if(strcmp(commands[0], "oscal") == 0)
{
printf("OSCAL: %u\n", coincellhell_read_oscal(hell));
}
else if(strcmp(commands[0], "seconds") == 0)
{
printf("Seconds: %u\n", coincellhell_get_seconds(hell));
}
else if(strcmp(commands[0], "git") == 0)
{
const uint8_t* rev = coincellhell_get_fw_git_revision(hell);
printf("Git: %x%x%x%x%x%x%x%x\n", rev[0], rev[1], rev[2], rev[3], rev[4], rev[5], rev[6], rev[7]);
}
else if(strcmp(commands[0], "watchdog") == 0)
{
ret = coincellhell_enable_watchdog(hell);
if(ret < 0)
puts("could not enable watchdog");
}
else if(strcmp(commands[0], "recal") == 0)
{
if(command_count < 2)
{
printf("Usage %s %s [BLOOEAN]\n", name, commands[0]);
return 2;
}
bool enable = strtol(commands[1], NULL, 10);
printf("Seting recal to %s\n", enable ? "true" : "false");
ret = coincellhell_set_periodic_recal(hell, enable);
if(ret < 0)
puts("could not enable watchdog");
}
else
{
printf("%s is not a valid command!", commands[0]);
print_commands();
}
return ret;
}
enum
{
INPUT_SUCESS = 0,
INPUT_NO_INPUT,
INPUT_TOO_LONG
};
static int get_input(char *prompt, char *buffer, size_t length)
{
int ch, extra;
if(prompt != NULL)
{
fputs(prompt, stdout);
fflush(stdout);
}
if(fgets (buffer, length, stdin) == NULL)
return INPUT_NO_INPUT;
if(buffer[strlen(buffer)-1] != '\n')
{
extra = 0;
while((ch = getchar()) != '\n' && ch != EOF)
extra = 1;
return (extra == 1) ? INPUT_TOO_LONG : INPUT_SUCESS;
}
buffer[strlen(buffer)-1] = '\0';
return INPUT_SUCESS;
}
int main(int argc, char* argv[])
{
struct config config = {};
argp_parse(&argp, argc, argv, 0, 0, &config);
if(config.list_commands)
{
print_commands();
return 0;
}
if(config.command_count < 1 && !(config.interactive || config.pipe))
{
printf("A command is required\n");
return 2;
}
struct coincellhell hell;
if(coincellhell_connect(&hell, config.serialSet ? config.serial : 0))
{
char serialStr[5];
snprintf(serialStr, sizeof(serialStr), "%04hu", config.serial);
printf("Can not connect to EIShell device%s%s\n",
config.serialSet ? " with serial " : "", config.serialSet ? serialStr : "");
return 1;
}
int ret = 0;
if(config.interactive || config.pipe)
{
char buffer[256];
while(true)
{
int ret = get_input(config.pipe ? NULL : "> ", buffer, sizeof(buffer));
if(ret == INPUT_NO_INPUT)
{
break;
}
else if(ret != INPUT_SUCESS)
{
puts("Invalid command");
continue;
}
char* commands[MAX_COMMANDS];
char* command = strtok(buffer, " \t");
size_t i = 0;
while(command && i < MAX_COMMANDS)
{
commands[i] = command;
command = strtok(NULL, " \t");
++i;
}
coincellhell_set_led(&hell, true);
int cmdRet = process_commands(commands, i, &hell, "");
coincellhell_set_led(&hell, false);
if(cmdRet == 0)
puts("OK");
else
printf("ERR %i\n", cmdRet);
}
}
else
{
ret = process_commands(config.commands, config.command_count, &hell, argv[0]);
}
coincellhell_disconnect(&hell);
return ret;
}