diff --git a/Makefile b/Makefile index 7e9dd00c..54601dfc 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,13 @@ all: DEBUG_BEHAVIOR= all: libax5043.a all: radioafsk all: radiocw +all: telem debug: DEBUG_BEHAVIOR = -DDEBUG_LOGGING debug: libax5043.a debug: radioafsk debug: radiocw +debug: telem rebuild: clean rebuild: all @@ -28,6 +30,7 @@ clean: rm -f */*/*.o rm -rf ax5043/doc/html rm -rf ax5043/doc/latex + rm -f telem docs: mkdir -p ax5043/doc; cd ax5043; doxygen Doxyfile @@ -57,7 +60,7 @@ radiocw: cw/cw_main.o radiocw: afsk/ax25.o radiocw: afsk/ax5043.o radiocw: afsk/send_afsk.o - gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o radiocw -L./ afsk/ax25.o afsk/ax5043.o afsk/send_afsk.o cw/cw_main.o -lwiringPi -lax5043 + gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o radiocw -L./ afsk/ax25.o afsk/ax5043.o afsk/send_afsk.o cw/cw_main.o -lwiringPi -lax5043 -lcurl radiopiglatin: libax5043.a radiopiglatin: piglatin/piglatin_main.o @@ -69,29 +72,30 @@ testax5043tx: transmit/transmit_main.o testax5043rx: libax5043.a testax5043rx: receive/receive_main.o - gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o testax5043rx -pedantic -Wall -Wextra -L./ receive/receive_main.o -lwiringPi -lax5043 + gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o testax5043rx -pedantic -Wall -Wextra -L./ receive/receive_main.o -lwiringPi -lax5043 -lcurl testax5043init: libax5043.a testax5043init: init/init_main.o - gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o testax5043init -pedantic -Wall -Wextra -L./ init/init_main.o -lwiringPi -lax5043 + gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o testax5043init -pedantic -Wall -Wextra -L./ init/init_main.o -lwiringPi -lax5043 -lcurl testax50432freq: libax5043.a testax50432freq: transmit2freq/transmit2freq_main.o - gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o testax50432freq -pedantic -Wall -Wextra -L./ transmit2freq/transmit2freq_main.o -lwiringPi -lax5043 + gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o testax50432freq -pedantic -Wall -Wextra -L./ transmit2freq/transmit2freq_main.o -lwiringPi -lax5043 -lcurl testafsktx: libax5043.a testafsktx: afsktx/ax25.o testafsktx: afsktx/ax5043.o testafsktx: afsktx/main.o - gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o testafsktx -pedantic -Wall -Wextra -L./ afsktx/ax25.o afsktx/ax5043.o afsktx/main.o -lwiringPi -lax5043 + gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o testafsktx -pedantic -Wall -Wextra -L./ afsktx/ax25.o afsktx/ax5043.o afsktx/main.o -lwiringPi -lax5043 -lcurl radioafsk: libax5043.a radioafsk: afsk/ax25.o radioafsk: afsk/ax5043.o radioafsk: afsk/main.o -radioafsk: afsk/ina219.h - gcc -std=gnu99 $(DEBUG_BEHAVIOR) -o radioafsk -pedantic -Wall -Wextra -L./ afsk/ax25.o afsk/ax5043.o afsk/main.o -lwiringPi -lax5043 + gcc $(DEBUG_BEHAVIOR) -o radioafsk -pedantic -Wall -Wextra -L./ afsk/ax25.o afsk/ax5043.o afsk/main.o -lwiringPi -lax5043 -lcurl +telem: afsk/telem.o + gcc $(DEBUG_BEHAVIOR) -o telem -pedantic -Wall -Wextra -L./ afsk/telem.o -lwiringPi ax5043/generated/configcommon.o: ax5043/generated/configcommon.c ax5043/generated/configcommon.o: ax5043/generated/configrx.h @@ -232,8 +236,13 @@ afsk/main.o: afsk/status.h afsk/main.o: afsk/ax5043.h afsk/main.o: afsk/ax25.h afsk/main.o: ax5043/spi/ax5043spi.h +afsk/main.o: afsk/Adafruit_INA219.h cd afsk; gcc -std=gnu99 $(DEBUG_BEHAVIOR) -I ../ax5043 -pedantic -Wconversion -Wall -Wextra -c main.c; cd .. +afsk/telem.o: afsk/telem.c +afsk/telem.o: afsk/Adafruit_INA219.h + cd afsk; gcc -std=gnu99 $(DEBUG_BEHAVIOR) -I ../ax5043 -pedantic -Wconversion -Wall -Wextra -c telem.c; cd .. + afsk/send_afsk.o: afsk/send_afsk.c afsk/send_afsk.o: afsk/send_afsk.h afsk/send_afsk.o: afsk/status.h diff --git a/afsk/ax25.c b/afsk/ax25.c index 15b127c2..cc3270c8 100644 --- a/afsk/ax25.c +++ b/afsk/ax25.c @@ -18,8 +18,11 @@ * along with this program. If not, see . */ +#include #include "ax25.h" #include +#include +#include #include "ax5043.h" #include "status.h" @@ -88,6 +91,55 @@ int ax25_tx_frame(ax25_conf_t *hax25, ax5043_conf_t *hax, memcpy(__tx_buffer, hax25->addr_field, hax25->addr_field_len); memcpy(__tx_buffer + hax25->addr_field_len, payload, len); + +#ifdef SATNOGS + printf("\n"); + char post_data[1024]; + char hex_data[512]; + char hex_octet[4]; + time_t t = time(NULL); + struct tm tm = *localtime(&t); + memset(post_data,0,strlen(post_data)); + memset(hex_data,0,strlen(hex_data)); + //printf("1:%s\n",post_data); + + int jj; + for(jj = 0; jj < 118; jj++) { + sprintf(hex_octet, "%02x",__tx_buffer[jj]); + strcat(hex_data, hex_octet); + } // Note assumes EDT, change offset (+4) to UTC + sprintf(post_data,"noradID=99999&source=KU2Y×tamp=%d-%d-%dT%d:%d:%d.500Z&frame=%s&locator=longLat&longitude=75.3492W&latitude=40.0376N&&azimuth=360&elevation=90.0", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, (tm.tm_hour + 4) % 24, tm.tm_min, tm.tm_sec, hex_data); + + printf("curl post data: %s\n",post_data); + + CURL *curl; + CURLcode res; + + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + if(curl) { + //curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); + curl_easy_setopt(curl, CURLOPT_URL, "https://db.satnogs.org/api/telemetry/"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); + + /* if we don't provide POSTFIELDSIZE, libcurl will strlen() by + itself */ + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(post_data)); + + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if(res != CURLE_OK) + fprintf(stderr, "ERROR: AX25.C curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + } +#endif return ax5043_tx_frame(hax, __tx_buffer, len + hax25->addr_field_len, hax25->preamble_len, hax25->postable_len, 1000); diff --git a/afsk/ax5043.c.bk b/afsk/ax5043.c.bk deleted file mode 100644 index f5b31b91..00000000 --- a/afsk/ax5043.c.bk +++ /dev/null @@ -1,1162 +0,0 @@ -/* - * A sample application transmitting AFSK 1200 baud - * - * Portions Copyright (C) 2018 Libre Space Foundation - * Portions Copyright (C) 2018 Jonathan Brandenburg - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "ax25.h" -#include "ax5043.h" -#include "status.h" -#include "utils.h" -#include "spi/ax5043spi.h" - -static uint8_t single_fifo_access = 0; - -static uint8_t __tx_buf[MAX_FRAME_LEN]; -static size_t __tx_buf_idx = 0; -static uint8_t __tx_fifo_chunk[AX5043_FIFO_MAX_SIZE]; -static uint32_t __tx_remaining = 0; - -/** - * FIFO command for the preamble. The third byte corresponds the length of - * the preamble and is set by the TX routine for every frame - */ -static uint8_t __preamble_cmd[4] = { AX5043_FIFO_REPEATDATA_CMD, -AX5043_FIFO_PKTSTART | AX5043_FIFO_RAW | AX5043_FIFO_NOCRC, 0, -AX25_SYNC_FLAG }; - -/** - * FIFO command for the postable. The third byte corresponds the length of - * the postable and is set by the TX routine for every frame - */ -static uint8_t __postamble_cmd[4] = { -AX5043_FIFO_REPEATDATA_CMD, -AX5043_FIFO_PKTSTART | AX5043_FIFO_PKTEND | AX5043_FIFO_RAW | AX5043_FIFO_NOCRC, - 0, AX25_SYNC_FLAG }; - -/** - * Indicates if a TX is currently active - */ -static volatile uint8_t __tx_active = 0; - -static ax5043_conf_t *__ax5043_conf = NULL; - -static inline int set_tx_black_magic_regs(); - -/** - * Checks if the AX5043 handler is valid - * @param conf the AX5043 configuration handler pointer - * @return 1 if it is valid 0 otherwise - */ -static uint8_t is_ax5043_conf_valid(ax5043_conf_t *conf) { - if (!conf || !conf->f_xtal) { - return 0; - } - return 1; -} - -/** - * Resets the AX5043 - * @param conf the AX5043 configuration handler - * @return 0 on success or appropriate negative error code - */ -int ax5043_reset_a(ax5043_conf_t *conf) { - int ret; - uint8_t val; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - conf->rf_init = 0; - usleep(100); - - /* Reset the chip using the appropriate register */ - val = BIT(7); - ret = ax5043_spi_write_8(conf, AX5043_REG_PWRMODE, val); - if (ret) { - return ret; - } - usleep(100); - /* Clear the reset bit, but keep REFEN and XOEN */ - ret = ax5043_spi_read_8(conf, &val, AX5043_REG_PWRMODE); - if (ret) { - return ret; - } - val &= (BIT(6) | BIT(5)); - ret = ax5043_spi_write_8(conf, AX5043_REG_PWRMODE, val); - if (ret) { - return ret; - } - usleep(100); - - ret = ax5043_set_power_mode(conf, POWERDOWN); - if (ret) { - return ret; - } - return PQWS_SUCCESS; -} - -/** - * Initialization routine for the AX5043 IC - * @param conf the AX5043 configuration handler - * @param spi the SPI handler - * @param f_xtal the frequency of the crystal or the TCXO - * @param vco the VCO mode - * @return 0 on success or appropriate negative error code - */ -int ax5043_init(ax5043_conf_t *conf, uint32_t f_xtal, vco_mode_t vco) { - int ret; - uint8_t revision; - uint8_t val; - printf("1 \n"); - - if (!conf) { - return -PQWS_INVALID_PARAM; - } - - /* Set the initial parameters */ - memset(conf, 0, sizeof(ax5043_conf_t)); - printf("2\n "); - switch (vco) { - case VCO_INTERNAL: - case VCO_EXTERNAL: - conf->vco = vco; - break; - default: - return -PQWS_INVALID_PARAM; - } - - conf->rf_init = 0; - conf->freqsel = -1; - conf->f_xtal = f_xtal; - if (conf->f_xtal > 24800000) { - conf->f_xtaldiv = 2; - } else { - conf->f_xtaldiv = 1; - } - printf("3\n "); - - ret = ax5043_reset_a(conf); - printf("4 \n"); - if (ret) { - return ret; - } - printf("5\n "); - - /* Try first to read the revision register of the AX5043 */ - ret = ax5043_spi_read_8(conf, &revision, AX5043_REG_REV); - printf("6 \n"); - if (ret) { - return ret; - } - - if (revision != AX5043_REV) { - return -PQWS_NO_RF_FOUND; - } - - /* To ensure communication try to write and read the scratch register */ - val = AX5043_SCRATCH_TEST; - printf("7 \n"); - ret = ax5043_spi_write_8(conf, AX5043_REG_SCRATCH, val); - printf("8 \n"); - if (ret) { - return ret; - } - - val = 0x0; - printf("9 \n"); - ret = ax5043_spi_read_8(conf, &val, AX5043_REG_SCRATCH); - printf("10 \n"); - if (ret) { - return ret; - } - - if (val != AX5043_SCRATCH_TEST) { - return -PQWS_NO_RF_FOUND; - } - - printf("11 \n"); - ret = ax5043_set_pll_params(conf); - printf("12 \n"); - if (ret) { - return ret; - } - - /* Write the performance register F35 based on the XTAL frequency */ - printf("13 \n"); - if (conf->f_xtaldiv == 1) { - ret = ax5043_spi_write_8(conf, 0xF35, 0x10); - } else { - ret = ax5043_spi_write_8(conf, 0xF35, 0x11); - } - printf("14 \n"); - if (ret) { - return ret; - } - - /* FIFO maximum chunk */ - printf("15 \n"); - ret = ax5043_spi_write_8(conf, AX5043_REG_PKTCHUNKSIZE, - AX5043_PKTCHUNKSIZE_240); - printf("15 \n"); - if (ret) { - return ret; - } - - /* Set RF parameters */ - printf("16 \n"); - ret = ax5043_freqsel(conf, FREQA_MODE); - printf("17 \n"); - if (ret) { - return ret; - } - - /* - * We use APRS for all transmitted frames. APRS is encapsulated in a - * AX.25 frame. For 9600 baudrate is FSK9600 G3RUH compatible modem - */ - printf("18 \n"); - ret = ax5043_aprs_framing_setup(conf); - printf("19 \n"); - if (ret) { - return ret; - } - - /* Setup TX only related parameters */ - printf("20 \n"); - ret = ax5043_conf_tx_path(conf); - printf("21 \n"); - if (ret) { - return ret; - } - - /* Set an internal copy for the ax5042_wait_for_transmit function */ - __ax5043_conf = conf; - - if (ret) { - return ret; - } - printf("22 \n"); - ax5043_enable_pwramp(conf, AX5043_EXT_PA_DISABLE); - printf("23 \n"); - return PQWS_SUCCESS; -} - -/** - * Performs TX specific configuration of the AX5043 - * @param conf the AX5043 configuration handler - * @return 0 on success or appropriate negative error code - */ -int ax5043_conf_tx_path(ax5043_conf_t *conf) { - printf("25 \n"); - int ret; - - printf("26 \n"); - ret = ax5043_set_tx_synth(conf); - printf("27 \n"); - if (ret) { - return ret; - } - - printf("28 \n"); - ret = ax5043_set_tx_baud(conf, TX_BAUDRATE); - printf("29 \n"); - if (ret) { - return ret; - } - - // printf("30 \n"); - ret = ax5043_set_tx_freq(conf, TX_FREQ_HZ); - printf("31\n "); - if (ret) { - return ret; - } - - /* Our TX is on single ended mode */ - //ret = ax5043_spi_write_8(conf, AX5043_REG_MODCFGA, - //AX5043_TX_SINGLE_ENDED); - //if (ret) { - // return ret; - //} - - /* Our TX is on double ended mode */ - printf("32 \n"); - ret = ax5043_spi_write_8(conf, AX5043_REG_MODCFGA, - AX5043_TX_DIFFERENTIAL); - printf("33 \n"); - if (ret) { - return ret; - } - - /* Set the rest of the performance registers for TX */ - printf("34 \n"); - ret = set_tx_black_magic_regs(conf); - printf("35 \n"); - if (ret) { - return ret; - } - - /* - * As our board has an external PA, reduce the output power to reduce - * the excess bandwidth emissions - */ - //ret = ax5043_spi_write_16(conf, AX5043_REG_TXPWRCOEFFB1, 0x01FF); - //if (ret) { - //return ret; - //} - - // Not using a PA, transmit half power - printf("36 \n"); - ret = ax5043_spi_write_16(conf, AX5043_REG_TXPWRCOEFFB1, 0x07FF); - printf("37 \n"); - if (ret) { - return ret; - } - - return PQWS_SUCCESS; -} - -/** - * Sets the power mode of the AX5043 - * @param conf the AX5043 configuration handler - * @param mode the power mode - * @return 0 on success or appropriate negative error code - */ -int ax5043_set_power_mode(ax5043_conf_t *conf, power_mode_t mode) { - int ret; - uint8_t val; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - /* Read the contents of the register */ - ret = ax5043_spi_read_8(conf, &val, AX5043_REG_PWRMODE); - if (ret) { - return ret; - } - - /* Keep REFEN and XOEN values */ - val &= (BIT(6) | BIT(5)); - - switch (mode) { - case POWERDOWN: - val |= AX5043_POWERDOWN; - break; - case DEEPSLEEP: - val |= AX5043_DEEPSLEEP; - break; - case STANDBY: - val |= AX5043_STANDBY; - break; - case FIFO_ENABLED: - val |= AX5043_FIFO_ENABLED; - break; - case RECEIVE_MODE: - val |= AX5043_RECEIVE_MODE; - break; - case RECEIVER_RUNNING: - val |= AX5043_RECEIVER_RUNNING; - break; - case RECEIVER_WOR: - val |= AX5043_RECEIVER_WOR; - break; - case TRANSMIT_MODE: - val |= AX5043_TRANSMIT_MODE; - break; - case FULLTX: - val |= AX5043_FULLTX; - break; - default: - return -PQWS_INVALID_PARAM; - } - return ax5043_spi_write_8(conf, AX5043_REG_PWRMODE, val); -} - -/** - * Sets the RF frequency of the TX. If the previous TX frequency is - * further enough than the new one, this function performs automatically - * auto-ranging. - * - * @param conf the AX5043 configuration handler - * @param freq the target RF frequency - * @return 0 on success or appropriate negative error code - */ -int ax5043_set_tx_freq(ax5043_conf_t *conf, uint32_t freq) { - int ret; - uint32_t prev_freq; - uint32_t reg_val; - uint8_t rfdiv = 0; - uint8_t pllcodediv = 0; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - /* Check the frequency range. The actual range depends on the VCO used */ - switch (conf->vco) { - case VCO_INTERNAL: - if (freq >= MIN_RF_FREQ_INT_VCO_RFDIV0 - && freq <= MAX_RF_FREQ_INT_VCO_RFDIV0) { - rfdiv = AX5043_RFDIV0; - } else if (freq >= MIN_RF_FREQ_INT_VCO_RFDIV1 - && freq <= MAX_RF_FREQ_INT_VCO_RFDIV1) { - rfdiv = AX5043_RFDIV1; - } else { - return -PQWS_INVALID_PARAM; - } - break; - case VCO_EXTERNAL: - fprintf(stderr, "ERROR: Unexpected use of external VCO\n"); - if (freq >= MIN_RF_FREQ_EXT_VCO_RFDIV0 - && freq <= MAX_RF_FREQ_EXT_VCO_RFDIV0) { - rfdiv = AX5043_RFDIV0; - } else if (freq >= MIN_RF_FREQ_EXT_VCO_RFDIV1 - && freq <= MAX_RF_FREQ_EXT_VCO_RFDIV1) { - rfdiv = AX5043_RFDIV1; - } else { - return -PQWS_INVALID_PARAM; - } - break; - default: - return -PQWS_INVALID_PARAM; - } - prev_freq = conf->tx_freq; - pllcodediv = rfdiv | (uint8_t) (conf->vco << 4); - - // Added by Jonathan Brandenburg - // Have an external inductor - pllcodediv |= BIT(5); - - ret = ax5043_spi_write_8(conf, AX5043_REG_PLLVCODIV, pllcodediv); - if (ret) { - return ret; - } - - /* Write properly the F34 performance register based on the RFDIV*/ - if (rfdiv == AX5043_RFDIV1) { - ret = ax5043_spi_write_8(conf, 0xF34, 0x28); - } else { - ret = ax5043_spi_write_8(conf, 0xF34, 0x08); - } - if (ret) { - return ret; - } - - /* - * Set the RF frequency - * Frequency should be avoided to be a multiple integer of the crystal - * frequency, so we always set to 1 the LSB - */ - reg_val = ((uint32_t) (((float) freq / (float) conf->f_xtal) * (1 << 24)) - | 0x1); - if (conf->freqsel == FREQA_MODE) { - ret = ax5043_spi_write_32(conf, AX5043_REG_FREQA3, reg_val); - } else { - ret = ax5043_spi_write_32(conf, AX5043_REG_FREQB3, reg_val); - } - if (ret) { - return ret; - } - - /* Considered that the frequency successfully changed */ - conf->tx_freq = freq; - - /* If the frequency difference is great enough perform autoranging */ - if (freq + 25000000 > prev_freq || freq - 25000000 < prev_freq) { - ax5043_autoranging(conf); - } - - return PQWS_SUCCESS; -} - -/** - * Set the TX baudrate - * @param conf the AX5043 configuration handler - * @param baud the baudrate - * @return 0 on success or negative error code - */ -int ax5043_set_tx_baud(ax5043_conf_t *conf, uint32_t baud) { - int ret = PQWS_SUCCESS; - uint32_t val; - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - val = (uint32_t) ((((float) baud) / (float) conf->f_xtal) * (1 << 24)) - | 0x1; - ret = ax5043_spi_write_24(conf, AX5043_REG_TXRATE2, val); - if (ret) { - return ret; - } - - conf->tx_baudrate = baud; - - /* Set the deviation to standard 3 KHz for FM */ - // For AFSK, FSKDEV = 0.858785 * fDeviation / fXTAL * 2^24 - // - val = (uint32_t) ((0.858785 * 3000.0f / (float) conf->f_xtal) * (1 << 24)) - | 0x1; - ret = ax5043_spi_write_24(conf, AX5043_REG_FSKDEV2, val); - if (ret) { - return ret; - } - - return PQWS_SUCCESS; -} - -/** - * Sets the currently used frequency registers (A or B) - * @param conf the AX5043 configuration handler - * @param f the frequency mode (A or B) - * @return 0 on success or appropriate negative error code - */ -int ax5043_freqsel(ax5043_conf_t *conf, freq_mode_t f) { - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - if (f != FREQA_MODE && f != FREQB_MODE) { - return -PQWS_INVALID_PARAM; - } - conf->freqsel = f; - return PQWS_SUCCESS; -} - -/** - * Sets the TX frequency synthesizer related configuration registers. - * @param conf the AX5043 configuration handler - * @return 0 on success or appropriate negative error code - */ -int ax5043_set_tx_synth(ax5043_conf_t *conf) { - int ret; - uint8_t val; - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - switch (conf->freqsel) { - case FREQA_MODE: - val = 0x0; - break; - case FREQB_MODE: - val = 1 << 7; - break; - default: - return -PQWS_INVALID_PARAM; - } - - /* Bypass external filter and use 100 kHZ loop bandwidth */ - val |= BIT(3) | BIT(0); - ret = ax5043_spi_write_8(conf, AX5043_REG_PLLLOOP, val); - if (ret) { - return ret; - } - - /* - * Set the charge pump current based on the loop bandwidth - * 68 uA @ 100 kHZ - */ - ret = ax5043_spi_write_8(conf, AX5043_REG_PLLCPI, (uint8_t) (68 / 8.5)); - if (ret) { - return ret; - } - ret = ax5043_spi_write_8(conf, AX5043_REG_XTALCAP, 0); - return ret; -} - -/** - * Sets the PLL related configuration registers. - * @param conf the AX5043 configuration handler - * @return 0 on success or appropriate negative error code - */ -int ax5043_set_pll_params(ax5043_conf_t *conf) { - int ret; - uint8_t i = 8; - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - /* Set VCO to manual */ - ret = ax5043_spi_write_8(conf, AX5043_REG_PLLVCOI, - AX5043_PLLVCOI_MANUAL | (1250 / 50)); - if (ret) { - return ret; - } - - /* - * According to the manual PLL ranging clock should be less than 1/10 - * of the PLL loop bandwidth. The smallest PLL bandwidth configuration - * is 100 kHz. - */ - // This this next line contains an error - //while (conf->f_xtal / (uint32_t) (1 << i) > 10000) { - while (conf->f_xtal / (uint32_t) (1 << i) > 100000) { - i++; - } - i = i > 15 ? 15 : i; - i = i < 8 ? 8 : i; - ret = ax5043_spi_write_8(conf, AX5043_REG_PLLRNGCLK, (uint8_t) (i - 8)); - return ret; -} - -/** - * Performs auto-ranging using the frequency registers configured by - * ax5043_freqsel(). - * - * @param conf the AX5043 configuration handler - * @return 0 on success or appropriate negative error code - */ -int ax5043_autoranging(ax5043_conf_t *conf) { - int ret = PQWS_SUCCESS; - uint16_t pllranging_reg; - uint8_t val = 0; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - switch (conf->freqsel) { - case FREQA_MODE: - pllranging_reg = AX5043_REG_PLLRANGINGA; - break; - case FREQB_MODE: - pllranging_reg = AX5043_REG_PLLRANGINGB; - break; - default: - return -PQWS_INVALID_PARAM; - } - - /* Write the initial VCO setting and start autoranging */ - val = BIT(4) | AX5043_VCOR_INIT; - ret = ax5043_spi_write_8(conf, pllranging_reg, val); - if (ret) { - return ret; - } - - usleep(10); - val = 0; - /* Wait until the autoranging is complete */ - while ((val & BIT(4)) == 0) { - ret = ax5043_spi_read_8(conf, &val, pllranging_reg); - if (ret) { - return ret; - } - } - - if (val & BIT(5)) { - return -PQWS_AX5043_AUTORANGING_ERROR; - } - - return PQWS_SUCCESS; -} - -/** - * - * @param conf the AX5043 configuration handler - * @return 0 on success or appropriate negative error code - */ -int ax5043_aprs_framing_setup(ax5043_conf_t *conf) { - int ret = PQWS_SUCCESS; - uint8_t val = 0; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - /* Set modulation */ - val = AX5043_MODULATION_AFSK; - ret = ax5043_spi_write_8(conf, AX5043_REG_MODULATION, val); - if (ret) { - return ret; - } - - // To set the space frequency, 1070 - // 1070 * 2^18 / fXTAL -> 1070 * 2^18 / 16000000 -> 18 -> 0x12 - - // To set the space frequency, 1200 - // 1200 * 2^18 / fXTAL -> 1200 * 2^18 / 16000000 -> 20 -> 0x14 - - ret = ax5043_spi_write_16(conf, AX5043_REG_AFSKSPACE1, 0x24); - if (ret) { - return ret; - } - - // To set the mark frequency, 1270 - // 1270 * 2^18 / fXTAL -> 1270 * 2^18 / 16000000 -> 21 -> 0x15 - - // To set the mark frequency, 1400 - // 1400 * 2^18 / fXTAL -> 1400 * 2^18 / 16000000 -> 23 -> 0x17 - - ret = ax5043_spi_write_16(conf, AX5043_REG_AFSKMARK1, 0x14); - if (ret) { - return ret; - } - - /* - * As we do not use any external filter, try to filter from - * the AX5043 the signal - */ - ret = ax5043_spi_write_8(conf, AX5043_REG_MODCFGF, - AX5043_FREQSHAPE_GAUSSIAN_BT_05); - if (ret) { - return ret; - } - - /* Set HDLC encoding: Differential = 1, Inverse = 1, Scrambling = 1 */ - //ax5043_spi_write_8(conf, AX5043_REG_ENCODING, - /* Set HDLC encoding: Differential = 1, Inverse = 1, Scrambling = 1 */ - ax5043_spi_write_8(conf, AX5043_REG_ENCODING, - AX5043_ENC_DIFF | AX5043_ENC_INV); - - /* HDLC framing */ - ax5043_spi_write_8(conf, AX5043_REG_FRAMING, - AX5043_HDLC_FRAMING | AX5043_CRC16_CCITT); - return ret; -} - -static int __tx_frame_end(ax5043_conf_t *conf) { - int ret; - - ax5043_enable_pwramp(conf, AX5043_EXT_PA_DISABLE); - - /* Set AX5043 to power down mode */ - ret = ax5043_set_power_mode(conf, POWERDOWN); - __tx_active = 0; - return ret; -} - -static int __tx_frame(ax5043_conf_t *conf, const uint8_t *in, uint32_t len, - uint8_t preamble_len, uint8_t postamble_len, uint32_t timeout_ms) { - int ret = PQWS_SUCCESS; - uint8_t data_cmd[3] = { AX5043_FIFO_VARIABLE_DATA_CMD, 0, 0 }; - size_t chunk_size = 0; - size_t avail; - uint8_t val; - uint32_t start = millis(); - - /* - * Apply preamble and postamble repetition length. Rest of the fields should - * remain unaltered - */ - __preamble_cmd[2] = preamble_len; - __postamble_cmd[2] = postamble_len; - - memcpy(__tx_fifo_chunk, __preamble_cmd, sizeof(__preamble_cmd)); - chunk_size = sizeof(__preamble_cmd); - __tx_buf_idx = 0; - - /* - * Always leave some space for the postamble. This greatly reduces the - * complexity of dealing with some corner cases - */ - avail = min_ul( - AX5043_FIFO_MAX_SIZE - sizeof(__preamble_cmd) - sizeof(data_cmd) - - sizeof(__postamble_cmd), len); - if (len == avail) { - data_cmd[1] = (uint8_t) (len + 1); - data_cmd[2] = AX5043_FIFO_PKTEND; - __tx_remaining = 0; - memcpy(__tx_fifo_chunk + chunk_size, data_cmd, sizeof(data_cmd)); - chunk_size += sizeof(data_cmd); - memcpy(__tx_fifo_chunk + chunk_size, in, len); - chunk_size += len; - /* - * At this point we are sure that the whole frame + postamble can fit in - * the FIFO chunk - */ - memcpy(__tx_fifo_chunk + chunk_size, __postamble_cmd, - sizeof(__postamble_cmd)); - chunk_size += sizeof(__postamble_cmd); - single_fifo_access = 1; - } else { - data_cmd[1] = (uint8_t) (avail + 1); - data_cmd[2] = 0; - memcpy(__tx_fifo_chunk + chunk_size, data_cmd, sizeof(data_cmd)); - chunk_size += sizeof(data_cmd); - memcpy(__tx_fifo_chunk + chunk_size, in, avail); - chunk_size += avail; - - memcpy(__tx_buf, in + avail, len - avail); - __tx_remaining = (uint32_t) (len - avail); - single_fifo_access = 0; - } - - /* Set AX5043 to FULLTX mode */ - ret = ax5043_set_power_mode(conf, FULLTX); - if (ret) { - return ret; - } - - ax5043_spi_wait_xtal(conf, 100); - - /* Wait for the FIFO to become ready */ - val = 0; - while (!val) { - ax5043_spi_read_8(conf, &val, AX5043_REG_POWSTAT); - /* Select only the modem power state */ - val &= AX5043_SVMODEM; - if (millis() - start > timeout_ms) { - ret = -PQWS_TIMEOUT; - break; - } - } - - /* Fire-up the first data to the FIFO */ - ret = ax5043_spi_write(conf, AX5043_REG_FIFODATA, __tx_fifo_chunk, - (uint32_t) chunk_size); - if (ret) { - return ret; - } - __tx_active = 1; - /* Commit to FIFO ! */ - ret = ax5043_spi_write_8(conf, AX5043_REG_FIFOSTAT, AX5043_FIFO_COMMIT_CMD); - - return ret; -} - -int ax5043_tx_frame(ax5043_conf_t *conf, const uint8_t *in, uint32_t len, - uint8_t preamble_len, uint8_t postamble_len, uint32_t timeout_ms) { - int ret = 0; - - /* Wait for the previous frame to be transmitted */ - while (__tx_active) { - ret++; - } - - ret = ax5043_enable_pwramp(conf, AX5043_EXT_PA_ENABLE); - if (ret) { - return ret; - } - - ret = __tx_frame(conf, in, len, preamble_len, postamble_len, timeout_ms); - return ret; -} - -/** - * Wait the crystal to become ready - * @param conf the AX5043 configuration handler - * @param timeout_ms the timeout in milliseconds - * @return 0 on success or appropriate negative error code - */ -int ax5043_spi_wait_xtal(ax5043_conf_t *conf, uint32_t timeout_ms) { - int ret; - uint8_t val = 0x0; - uint32_t start = millis(); - - while (!val) { - ret = ax5043_spi_read_8(conf, &val, AX5043_REG_XTALSTATUS); - if (ret) { - return ret; - } - if ((millis() - start) > timeout_ms) { - return -PQWS_TIMEOUT; - } - } - return 0; -} - -int ax5043_spi_read_8(ax5043_conf_t *conf, uint8_t *out, uint16_t reg) { - int ret = PQWS_SUCCESS; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - *out = ax5043ReadReg(reg); - - return ret; -} - -int ax5043_spi_read_16(ax5043_conf_t *conf, uint16_t *out, uint16_t reg) { - int ret = PQWS_SUCCESS; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - *out = ax5043ReadReg2(reg); - - return ret; -} - -int ax5043_spi_read_24(ax5043_conf_t *conf, uint32_t *out, uint16_t reg) { - int ret = PQWS_SUCCESS; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - *out = ax5043ReadReg3(reg); - - return ret; -} - -int ax5043_spi_read_32(ax5043_conf_t *conf, uint32_t *out, uint16_t reg) { - int ret = PQWS_SUCCESS; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - *out = ax5043ReadReg4(reg); - - return ret; -} - -int ax5043_spi_write(ax5043_conf_t *conf, uint16_t reg, const uint8_t *in, - uint32_t len) { - int ret = PQWS_SUCCESS; - - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - ax5043WriteRegN(reg, in, len); - - return ret; -} - -int ax5043_spi_write_8(ax5043_conf_t *conf, uint16_t reg, uint8_t in) { - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - //printf("Reg\t%04x\t%02x\n", reg, in); - - ax5043WriteReg(reg, in); - - return PQWS_SUCCESS; -} - -int ax5043_spi_write_16(ax5043_conf_t *conf, uint16_t reg, uint16_t in) { - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - //printf("Reg\t%04x\t%02x\n", reg, (in >> 8)&0xFF); - //printf("Reg\t%04x\t%02x\n", reg+1, (in >> 0)&0xFF); - - ax5043WriteReg2(reg, in); - - return PQWS_SUCCESS; -} - -int ax5043_spi_write_24(ax5043_conf_t *conf, uint16_t reg, uint32_t in) { - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - //printf("Reg\t%04x\t%02x\n", reg, (in >> 16)&0xFF); - //printf("Reg\t%04x\t%02x\n", reg+1, (in >> 8)&0xFF); - //printf("Reg\t%04x\t%02x\n", reg+2, (in >> 0)&0xFF); - - ax5043WriteReg3(reg, in); - - return PQWS_SUCCESS; -} - -int ax5043_spi_write_32(ax5043_conf_t *conf, uint16_t reg, uint32_t in) { - if (!is_ax5043_conf_valid(conf)) { - return -PQWS_INVALID_PARAM; - } - - //printf("Reg\t%04x\t%02x\n", reg, (in >> 24)&0xFF); - //printf("Reg\t%04x\t%02x\n", reg+1, (in >> 16)&0xFF); - //printf("Reg\t%04x\t%02x\n", reg+2, (in >> 8)&0xFF); - //printf("Reg\t%04x\t%02x\n", reg+3, (in >> 0)&0xFF); - - ax5043WriteReg4(reg, in); - - return PQWS_SUCCESS; -} - -/** - * Sets properly some undocumented TX registers - * @param conf the AX5043 configuration handler - * @return 0 on success or appropriate negative error code - */ -static inline int set_tx_black_magic_regs(ax5043_conf_t *conf) { - int ret; - ret = ax5043_spi_write_8(conf, 0xF00, 0x0F); - if (ret) { - return ret; - } - - ret = ax5043_spi_write_8(conf, 0xF0C, 0x0); - if (ret) { - return ret; - } - - // Added by Jonathan Brandenburg - ret = ax5043_spi_write_8(conf, 0xF0D, 0x03); - if (ret) { - return ret; - } - - // Added by Jonathan Brandenburg - ret = ax5043_spi_write_8(conf, 0xF10, 0x03); - if (ret) { - return ret; - } - - // The following line is used for a TCXO - //ret = ax5043_spi_write_8(conf, 0xF11, 0x0); - // The following line is used for a crystal - ret = ax5043_spi_write_8(conf, 0xF11, 0x07); - if (ret) { - return ret; - } - - ret = ax5043_spi_write_8(conf, 0xF1C, 0x07); - if (ret) { - return ret; - } - - ret = ax5043_spi_write_8(conf, 0xF44, 0x24); - if (ret) { - return ret; - } - - /* Dafuq? Got it from RadioLab */ - ret = ax5043_spi_write_8(conf, 0xF18, 0x06); - return ret; -} - -/** - * Enables/Disables the power amplifier pin - * @param conf the AX5043 configuration handler - * @param enable 1 to enable 0 to disable - * @return 0 on success or appropriate negative error code - */ -int ax5043_enable_pwramp(ax5043_conf_t *conf, uint8_t enable) { - int ret; - ax5043_set_antsel(conf, enable); - ret = ax5043_spi_write_8(conf, AX5043_REG_PWRAMP, ~enable & 0x1); - - if (ret) { - usleep(PWRAMP_RAMP_PERIOD_US); - } - return ret; -} - -int ax5043_set_antsel(ax5043_conf_t *conf, uint8_t val) { - return ax5043_spi_write_8(conf, AX5043_REG_PINFUNCANTSEL, val & 0x1); -} - -/** - * Wait for the AX5043 to finish transmitting, putting new data in the FIFO as space becomes available - * @return 0 on success, or appropriate negative error code - */ -int ax5043_wait_for_transmit() { - if (!single_fifo_access) { - while (__tx_active) { - static int transmittedPostamble = 0; - - //usleep(100); - - int ret; - uint8_t data_cmd[3] = { AX5043_FIFO_VARIABLE_DATA_CMD, 0, 0 }; - size_t avail; - size_t chunk_size; - - if (!__ax5043_conf) { - return -PQWS_INVALID_PARAM; - } - - /* Determine if TX is done */ - uint8_t radiostate = 0; - ret = ax5043_spi_read_8(__ax5043_conf, &radiostate, - AX5043_REG_RADIOSTATE); - if (ret) { - return ret; - } - radiostate &= 0x0f; - - if (radiostate == 0) { - /* tx is done */ - __tx_frame_end(__ax5043_conf); - transmittedPostamble = 0; - printf("INFO: TX done\n"); - return PQWS_SUCCESS; - } - - /* Determine FIFO free space */ - uint16_t fifofree = 0; - ret = ax5043_spi_read_16(__ax5043_conf, &fifofree, - AX5043_REG_FIFOFREE1); - if (ret) { - return ret; - } - - /* If FIFO has free space fill in data */ - if (fifofree > AX5043_FIFO_FREE_THR && (__tx_remaining || !transmittedPostamble)) { - - /* Always left some space for the postamble for a simplified logic */ - avail = min_ul( - AX5043_FIFO_FREE_THR - sizeof(data_cmd) - sizeof(__postamble_cmd), - __tx_remaining); - - data_cmd[1] = (uint8_t) (avail + 1); - chunk_size = sizeof(data_cmd) + avail; - memcpy(__tx_fifo_chunk + sizeof(data_cmd), __tx_buf + __tx_buf_idx, - avail); - - if (avail == __tx_remaining) { - transmittedPostamble = 1; - - data_cmd[2] = AX5043_FIFO_PKTEND; - memcpy(__tx_fifo_chunk + chunk_size, __postamble_cmd, - sizeof(__postamble_cmd)); - chunk_size += sizeof(__postamble_cmd); - } - memcpy(__tx_fifo_chunk, data_cmd, sizeof(data_cmd)); - ax5043_spi_write(__ax5043_conf, AX5043_REG_FIFODATA, - __tx_fifo_chunk, (uint32_t) chunk_size); - /* Commit to FIFO ! */ - ret = ax5043_spi_write_8(__ax5043_conf, AX5043_REG_FIFOSTAT, - AX5043_FIFO_COMMIT_CMD); - - __tx_remaining -= (uint32_t) avail; - __tx_buf_idx += avail; - - } - } - } else { - while (__tx_active) { - int ret; - /* Determine if TX is done */ - uint8_t radiostate = 0; - ret = ax5043_spi_read_8(__ax5043_conf, &radiostate, - AX5043_REG_RADIOSTATE); - if (ret) { - return ret; - } - radiostate &= 0x0f; - if (radiostate == 0) { - /* tx is done */ - __tx_active = 0; - printf("INFO: TX done\n"); - } - } - } - - return PQWS_SUCCESS; -} diff --git a/afsk/main.c b/afsk/main.c index f626f01e..bfd86ffa 100644 --- a/afsk/main.c +++ b/afsk/main.c @@ -1,5 +1,5 @@ /* - * Transmits CubeSat Telemetry at 440MHz in AO-7 format + * Transmits CubeSat Telemetry at 434.9MHz in AO-7 format * * Copyright Alan B. Johnston * @@ -17,9 +17,12 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * INA219 Raspberry Pi wiringPi code is based on Adafruit Arduino wire code + * from https://github.com/adafruit/Adafruit_INA219. */ -#include //Needed for I2C port +#include #include #include #include @@ -31,31 +34,23 @@ #include #include #include -#include "ina219.h" - -#define CALLSIGN "" // Put your callsign here! -#define VBATT 15 -#define ADC5 17 -#define ADC6 18 -#define ADC7 19 -#define ADC8 20 -#define TIME 8 -#define UCTEMP 30 -#define UPTIME_SEC 8 +#include +#include "../Adafruit_INA219/Adafruit_INA219.h" // From Adafruit INA219 library for Arduino + #define A 1 #define B 2 #define C 3 #define D 4 -#define SENSOR_40 0 -#define SENSOR_41 3 -#define SENSOR_44 6 -#define SENSOR_45 9 -#define SENSOR_4A 12 -#define VOLTAGE 0 -#define CURRENT 1 -#define POWER 2 -#define VBATT 15 +#define PLUS_X 0 +#define PLUS_Y 1 +#define PLUS_Z 2 +#define BAT 3 +#define MINUS_X 4 +#define MINUS_Y 5 +#define MINUS_Z 6 +#define BUS 7 +#define OFF -1 uint32_t tx_freq_hz = 434900000 + FREQUENCY_OFFSET; uint32_t tx_channel = 0; @@ -64,42 +59,127 @@ ax5043_conf_t hax5043; ax25_conf_t hax25; static void init_rf(); +int twosToInt(int val, int len); int get_tlm(int tlm[][5]); -long int timestamp; void config_x25(); void trans_x25(); -//long int timestamp; -int tempSensor; - int upper_digit(int number); int lower_digit(int number); -int charging = 0; -uint16_t config = (0x2000 | 0x1800 | 0x0180 | 0x0018 | 0x0007 ); +struct SensorConfig { + int fd; + uint16_t config; + int calValue; + int powerMultiplier; + int currentDivider; +}; + +struct SensorData { + double current; + double voltage; + double power; +}; + +/** + * @brief Read the data from one of the i2c current sensors. + * + * Reads the current data from the requested i2c current sensor configuration and + * stores it into a SensorData struct. An invalid file descriptor (i.e. less than zero) + * results in a SensorData struct being returned that has both its #current and #power members + * set to NAN. + * + * @param sensor A structure containing sensor configuration including the file descriptor. + * @return struct SensorData A struct that contains the current, voltage, and power readings + * from the requested sensor. + */ +struct SensorData read_sensor_data(struct SensorConfig sensor) { + struct SensorData data = { + .current = NAN, + .voltage = NAN, + .power = NAN }; + + if (sensor.fd < 0) { + return data; + } + // doesn't read negative currents accurately, shows -0.1mA + wiringPiI2CWriteReg16(sensor.fd, INA219_REG_CALIBRATION, sensor.calValue); + wiringPiI2CWriteReg16(sensor.fd, INA219_REG_CONFIG, sensor.config); + wiringPiI2CWriteReg16(sensor.fd, INA219_REG_CALIBRATION, sensor.calValue); + int value = wiringPiI2CReadReg16(sensor.fd, INA219_REG_CURRENT); + data.current = (float) twosToInt(value, 16) / (float) sensor.currentDivider; + + wiringPiI2CWrite(sensor.fd, INA219_REG_BUSVOLTAGE); + delay(1); // Max 12-bit conversion time is 586us per sample + value = (wiringPiI2CRead(sensor.fd) << 8 ) | wiringPiI2CRead (sensor.fd); + data.voltage = ((float)(value >> 3) * 4) / 1000; + // power has very low resolution, seems to step in 512mW values + data.power = (float) wiringPiI2CReadReg16(sensor.fd, INA219_REG_POWER) * (float) sensor.powerMultiplier; + + return data; +} + +/** + * @brief Configures an i2c current sensor. + * + * Calculates the configuration values of the i2c sensor so that + * current, voltage, and power can be read using read_sensor_data. + * Supports 16V 400mA and 16V 2.0A settings. + * + * @param sensor A file descriptor that can be used to read from the sensor. + * @param milliAmps The mA configuration, either 400mA or 2A are supported. + * @return struct SensorConfig A struct that contains the configuraton of the sensor. + */ +//struct SensorConfig config_sensor(int sensor, int milliAmps) { +struct SensorConfig config_sensor(char *bus, int address, int milliAmps) { + struct SensorConfig data; + + if (access(bus, W_OK | R_OK) < 0) { // Test if I2C Bus is missing + printf("ERROR: %s bus not present \n", bus); + data.fd = OFF; + return (data); + } + + data.fd = wiringPiI2CSetupInterface(bus, address); + + data.config = INA219_CONFIG_BVOLTAGERANGE_32V | + INA219_CONFIG_GAIN_1_40MV | + INA219_CONFIG_BADCRES_12BIT | + INA219_CONFIG_SADCRES_12BIT_1S_532US | + INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; + + if (milliAmps == 400) { // INA219 16V 400mA configuration + data.calValue = 8192; + data.powerMultiplier = 1; + data.currentDivider = 20; // 40; in Adafruit config + } + else { // INA219 16V 2A configuration + data.calValue = 40960; + data.powerMultiplier = 2; + data.currentDivider = 10; // 20; in Adafruit config + } + + #ifdef DEBUG_LOGGING + printf("Sensor %s %x configuration: %d %d %d %d %d\n", bus, address, data.fd, + data.config, data.calValue, data.currentDivider, data.powerMultiplier); + #endif + return data; +} + +struct SensorConfig sensor[8]; // 7 current sensors in Solar Power PCB plus one in MoPower UPS V2 +struct SensorData reading[8]; // 7 current sensors in Solar Power PCB plus one in MoPower UPS V2 +struct SensorConfig tempSensor; -int x_fd; // I2C bus 0 address 0x40 -int x_powerMultiplier; -int x_currentDivider; -int x_calValue; -int y_fd; // I2C bus 0 address 0x41 -int z_fd; // I2C bos 0 address 0x44 char src_addr[5] = ""; char dest_addr[5] = "CQ"; int main(int argc, char *argv[]) { if (argc > 1) { - strcpy(src_addr, argv[1]); - } - wiringPiSetup () ; - pinMode (0, OUTPUT) ; - int blink; - for (blink = 1; blink < 4 ;blink++) - { - digitalWrite (0, HIGH) ; delay (500) ; - digitalWrite (0, LOW) ; delay (500) ; + strcpy(src_addr, argv[1]); } - digitalWrite (0, HIGH) ; + + wiringPiSetup (); + pinMode (0, OUTPUT); setSpiChannel(SPI_CHANNEL); setSpiSpeed(SPI_SPEED); @@ -108,87 +188,25 @@ int main(int argc, char *argv[]) { int tlm[7][5]; memset(tlm, 0, sizeof tlm); - timestamp = time(NULL); - - int file_i2c = access("/dev/i2c-3", W_OK | R_OK); - //char *filenam1e = (char*)"/dev/i2c-3"; - if (file_i2c < 0) - { - fprintf(stderr,"ERROR: /dev/ic2-3 bus not present\n"); - tempSensor = -1; - } else - { - tempSensor = wiringPiI2CSetupInterface("/dev/i2c-3", 0x48); - } - - #ifdef DEBUG_LOGGING - fprintf(stderr,"tempSensor: %d \n",tempSensor); - #endif - - int arduinoI2C = access("/dev/i2c-0", W_OK | R_OK); - if (arduinoI2C < 0) - { - fprintf(stderr,"ERROR: /dev/i2c-0 bus not present\n"); - } else { - arduinoI2C = wiringPiI2CSetupInterface("/dev/i2c-0", 0x4B); - - #ifdef DEBUG_LOGGING - fprintf(stderr,"arduinoI2C: %d\n", arduinoI2C); - #endif - - if (arduinoI2C > 0) { - if(wiringPiI2CReadReg16(arduinoI2C,0) < 0) { - arduinoI2C = -1; // Disable reading of Arduino payload information - fprintf(stderr,"Arduino payload not present\n"); - } - } else { - fprintf(stderr,"Arduino payload not present\n"); - } - } - -// new INA219 current reading code - - x_calValue = 8192; - x_powerMultiplier = 1; - x_currentDivider = 20; - config = INA219_CONFIG_BVOLTAGERANGE_16V | - INA219_CONFIG_GAIN_40MV | - INA219_CONFIG_BADCRES_12BIT | - INA219_CONFIG_SADCRES_12BIT_4S_2130US | - //INA219_CONFIG_SADCRES_12BIT_1S_532US | - INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; - - file_i2c = access("/dev/i2c-0", W_OK | R_OK); - if (file_i2c < 0) - { - fprintf(stderr,"ERROR: /dev/ic2-0 bus not present\n"); - x_fd = -1; // Disable reading -X, -Y, and -Z telemetry - y_fd = -1; - z_fd = -1; - } else - { - x_fd = wiringPiI2CSetupInterface("/dev/i2c-0", 0x40); - y_fd = wiringPiI2CSetupInterface("/dev/i2c-0", 0x41); - z_fd = wiringPiI2CSetupInterface("/dev/i2c-0", 0x44); - - #ifdef DEBUG_LOGGING - fprintf(stderr, "Opening of -X fd %d\n", x_fd); - fprintf(stderr, "Opening of -Y fd %d\n", y_fd); - fprintf(stderr, "Opening of -Z fd %d\n", z_fd); - #endif - } + tempSensor = config_sensor("/dev/i2c-3", 0x48, 0); + + sensor[PLUS_X] = config_sensor("/dev/i2c-1", 0x40, 400); + sensor[PLUS_Y] = config_sensor("/dev/i2c-1", 0x41, 400); + sensor[PLUS_Z] = config_sensor("/dev/i2c-1", 0x44, 400); + sensor[BAT] = config_sensor("/dev/i2c-1", 0x45, 400); + sensor[BUS] = config_sensor("/dev/i2c-1", 0x4a, 2000); + sensor[MINUS_X] = config_sensor("/dev/i2c-0", 0x40, 400); + sensor[MINUS_Y] = config_sensor("/dev/i2c-0", 0x41, 400); + sensor[MINUS_Z] = config_sensor("/dev/i2c-0", 0x44, 400); int ret; uint8_t data[1024]; - tx_freq_hz -= tx_channel * 50000 + 85000; // subtracting rx offset of 90kHz + tx_freq_hz -= tx_channel * 50000; init_rf(); -// ax25_init(&hax25, (uint8_t *) "CubeSatSim", '2', (uint8_t *) CALLSIGN, '2', -// ax25_init(&hax25, (uint8_t *) "CQ", '1', (uint8_t *) CALLSIGN, '1', - - ax25_init(&hax25, (uint8_t *) dest_addr, '1', (uint8_t *) src_addr, '1', + ax25_init(&hax25, (uint8_t *) dest_addr, '1', (uint8_t *) src_addr, '1', AX25_PREAMBLE_LEN, AX25_POSTAMBLE_LEN); @@ -211,16 +229,11 @@ int main(int argc, char *argv[]) { char header_str[] = "\x03\xf0hi hi "; strcpy(str, header_str); - // printf("%s-1>CQ-1:hi hi ", CALLSIGN); printf("%s-1>%s-1:hi hi ", (uint8_t *)src_addr, (uint8_t *)dest_addr); int channel; for (channel = 1; channel < 7; channel++) { - #ifdef DEBUG_LOGGING - printf("%d %d %d %d \n", tlm[channel][1], tlm[channel][2], tlm[channel][3], tlm[channel][4]); - #endif - sprintf(tlm_str, "%d%d%d %d%d%d %d%d%d %d%d%d ", channel, upper_digit(tlm[channel][1]), lower_digit(tlm[channel][1]), channel, upper_digit(tlm[channel][2]), lower_digit(tlm[channel][2]), @@ -229,33 +242,10 @@ int main(int argc, char *argv[]) { printf("%s",tlm_str); strcat(str, tlm_str); } - - if (arduinoI2C > 0) { /* Read Arduino payload */ - for(int reg = 0; reg < 4; reg++) { - sprintf(tlm_str, " %04x",wiringPiI2CReadReg16(arduinoI2C,reg)); - #ifdef DEBUG_LOGGING - printf("%s \n",tlm_str); - #endif - strcat(str,tlm_str); /* Append payload telemetry */ - printf("%s",tlm_str); - usleep(100000); - } - } printf("\n"); digitalWrite (0, LOW); -/* -char cmdbuffer[1000]; - - if (charging) { - FILE* file1 = popen("/home/pi/mopower/mpcmd LED_STAT=1", "r"); - fgets(cmdbuffer, 999, file1); - pclose(file1); - -// printf("LED state: %s\n", cmdbuffer); - } -*/ #ifdef DEBUG_LOGGING fprintf(stderr,"INFO: Transmitting X.25 packet\n"); #endif @@ -271,13 +261,6 @@ char cmdbuffer[1000]; ax5043_wait_for_transmit(); digitalWrite (0, HIGH); -/* - FILE* file2 = popen("/home/pi/mopower/mpcmd LED_STAT=0", "r"); - fgets(cmdbuffer, 999, file2); - pclose(file2); - -// printf("LED state: %s\n", cmdbuffer); -*/ if (ret) { fprintf(stderr, "ERROR: Failed to transmit entire AX.25 frame with error code %d\n", @@ -327,85 +310,40 @@ int upper_digit(int number) { } int get_tlm(int tlm[][5]) { - // Reading I2C voltage and current sensors - char cmdbuffer[1000]; - FILE* file = popen("sudo python /home/pi/CubeSatSim/python/readcurrent.py 2>&1", "r"); - fgets(cmdbuffer, 999, file); - pclose(file); - #ifdef DEBUG_LOGGING - fprintf(stderr,"I2C Sensor data: %s\n", cmdbuffer); - #endif - - char ina219[16][20]; // voltage, currents, and power from the INA219 current sensors x4a, x40, x41, x44, and x45. - int i = 0; - char * data2 = strtok (cmdbuffer," "); - - while (data2 != NULL) { - strcpy(ina219[i], data2); +// Reading I2C voltage and current sensors + int count; + for (count = 0; count < 8; count++) + { + reading[count] = read_sensor_data(sensor[count]); #ifdef DEBUG_LOGGING - printf ("ina219[%d]=%s\n",i,ina219[i]); + printf("Read sensor[%d] % 4.2fV % 6.1fmA % 6.1fmW \n", + count, reading[count].voltage, reading[count].current, reading[count].power); #endif - data2 = strtok (NULL, " "); - i++; - } - -// read i2c current sensors // - double current = 0, power = 0, y_current = 0, y_power = 0, z_current = 0, z_power = 0; - if (x_fd != -1) { - wiringPiI2CWriteReg16(x_fd, INA219_REG_CALIBRATION, x_calValue); - wiringPiI2CWriteReg16(x_fd, INA219_REG_CONFIG, config); - wiringPiI2CWriteReg16(x_fd, INA219_REG_CALIBRATION, x_calValue); - current = wiringPiI2CReadReg16(x_fd, INA219_REG_CURRENT) / x_currentDivider; - power = wiringPiI2CReadReg16(x_fd, INA219_REG_POWER) * x_powerMultiplier; - - wiringPiI2CWriteReg16(y_fd, INA219_REG_CALIBRATION, x_calValue); - wiringPiI2CWriteReg16(y_fd, INA219_REG_CONFIG, config); - wiringPiI2CWriteReg16(y_fd, INA219_REG_CALIBRATION, x_calValue); - y_current = wiringPiI2CReadReg16(y_fd, INA219_REG_CURRENT) / x_currentDivider; - y_power = wiringPiI2CReadReg16(y_fd, INA219_REG_POWER) * x_powerMultiplier; - - wiringPiI2CWriteReg16(z_fd, INA219_REG_CALIBRATION, x_calValue); - wiringPiI2CWriteReg16(z_fd, INA219_REG_CONFIG, config); - wiringPiI2CWriteReg16(z_fd, INA219_REG_CALIBRATION, x_calValue); - z_current = wiringPiI2CReadReg16(z_fd, INA219_REG_CURRENT) / x_currentDivider; - z_power = wiringPiI2CReadReg16(z_fd, INA219_REG_POWER) * x_powerMultiplier; } - #ifdef DEBUG_LOGGING - printf("-X 0x40 current %4.2f power %4.2f -Y 0x41 current %4.2f power %4.2f -Z 0x44 current %4.2f power %4.2f \n", - current, - power, - y_current, - y_power, - z_current, - z_power); - printf("1B: ina219[%d]: %s val: %f \n", SENSOR_40 + CURRENT, ina219[SENSOR_40 + CURRENT], strtof(ina219[SENSOR_40 + CURRENT], NULL)); - #endif - - tlm[1][A] = (int)(strtof(ina219[SENSOR_4A + CURRENT], NULL) / 15 + 0.5) % 100; // Current of 5V supply to Pi - tlm[1][B] = (int) (99.5 - strtof(ina219[SENSOR_40 + CURRENT], NULL)/10) % 100; // +X current [4] - tlm[1][C] = (int) (99.5 - current/10) % 100; // X- current [10] - tlm[1][D] = (int) (99.5 - strtof(ina219[SENSOR_41 + CURRENT], NULL)/10) % 100; // +Y current [7] - - tlm[2][A] = (int) (99.5 - y_current/10) % 100; // -Y current [10] - tlm[2][B] = (int) (99.5 - strtof(ina219[SENSOR_44 + CURRENT], NULL)/10) % 100; // +Z current [10] // was 70/2m transponder power, AO-7 didn't have a Z panel - tlm[2][C] = (int) (99.5 - z_current/10) % 100; // -Z current (was timestamp) - -// tlm[2][C] = (int)((time(NULL) - timestamp) / 15) % 100; - tlm[2][D] = (int)(50.5 + strtof(ina219[SENSOR_45 + CURRENT], NULL)/10.0) % 100; // NiMH Battery current - - tlm[3][A] = abs((int)((strtof(ina219[SENSOR_45 + VOLTAGE], NULL) * 10) - 65.5) % 100); - tlm[3][B] = (int)(strtof(ina219[SENSOR_4A + VOLTAGE], NULL) * 10.0) % 100; // 5V supply to Pi - - if (tempSensor != -1) { - int tempValue = wiringPiI2CReadReg16(tempSensor, 0); - #ifdef DEBUG_LOGGING - printf("Temp Sensor Read: %x\n", tempValue); - #endif - + + tlm[1][A] = (int)(reading[BUS].voltage /15.0 + 0.5) % 100; // Current of 5V supply to Pi + tlm[1][B] = (int) (99.5 - reading[PLUS_X].current/10.0) % 100; // +X current [4] + tlm[1][C] = (int) (99.5 - reading[MINUS_X].current/10.0) % 100; // X- current [10] + tlm[1][D] = (int) (99.5 - reading[PLUS_Y].current/10.0) % 100; // +Y current [7] + + tlm[2][A] = (int) (99.5 - reading[MINUS_Y].current/10.0) % 100; // -Y current [10] + tlm[2][B] = (int) (99.5 - reading[PLUS_Z].current/10.0) % 100; // +Z current [10] // was 70/2m transponder power, AO-7 didn't have a Z panel + tlm[2][C] = (int) (99.5 - reading[MINUS_Z].current/10.0) % 100; // -Z current (was timestamp) + tlm[2][D] = (int)(50.5 + reading[BAT].current/10.0) % 100; // NiMH Battery current + + tlm[3][A] = abs((int)((reading[BAT].voltage * 10.0) - 65.5) % 100); + tlm[3][B] = (int)(reading[BUS].voltage * 10.0) % 100; // 5V supply to Pi + + if (tempSensor.fd != OFF) { + int tempValue = wiringPiI2CReadReg16(tempSensor.fd, 0); uint8_t upper = (uint8_t) (tempValue >> 8); uint8_t lower = (uint8_t) (tempValue & 0xff); float temp = (float)lower + ((float)upper / 0x100); - + + #ifdef DEBUG_LOGGING + printf("Temp Sensor Read: %6.1f\n", temp); + #endif + tlm[4][A] = (int)((95.8 - temp)/1.48 + 0.5) % 100; } @@ -414,9 +352,11 @@ int get_tlm(int tlm[][5]) { double cpuTemp; fscanf (cpuTempSensor, "%lf", &cpuTemp); cpuTemp /= 1000; + #ifdef DEBUG_LOGGING printf("CPU Temp Read: %6.1f\n", cpuTemp); #endif + tlm[4][B] = (int)((95.8 - cpuTemp)/1.48 + 0.5) % 100; fclose (cpuTempSensor); } @@ -437,3 +377,12 @@ int get_tlm(int tlm[][5]) { return 0; } + +int twosToInt(int val,int len) { // Convert twos compliment to integer +// from https://www.raspberrypi.org/forums/viewtopic.php?t=55815 + + if(val & (1 << (len - 1))) + val = val - (1 << len); + + return(val); +} diff --git a/afsk/main.c.x25_only b/afsk/main.c.x25_only deleted file mode 100644 index 7a67a49c..00000000 --- a/afsk/main.c.x25_only +++ /dev/null @@ -1,85 +0,0 @@ -/* - * A sample application transmitting AFSK at 1200 baud - * - * Portions Copyright (C) 2018 Jonathan Brandenburg - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include "status.h" -#include "ax5043.h" -#include "ax25.h" -#include "spi/ax5043spi.h" - -ax5043_conf_t hax5043; -ax25_conf_t hax25; - -static void init_rf(); - -int main(void) { - setSpiChannel(SPI_CHANNEL); - setSpiSpeed(SPI_SPEED); - initializeSpi(); - - init_rf(); - - ax25_init(&hax25, (uint8_t *) "CQ", '2', (uint8_t *) "DX", '2', - AX25_PREAMBLE_LEN, - AX25_POSTAMBLE_LEN); - - int ret; - uint8_t data[1024]; - // 0x03 is a UI frame - // 0x0F is no Level 3 protocol - const char *str = "\x03\x0fThis is an AX.25 Packet from CubeSatSim!!!"; - - /* Infinite loop */ - for (;;) { - sleep(2); - printf("INFO: Transmitting a packet\n"); - - memcpy(data, str, strnlen(str, 256)); - ret = ax25_tx_frame(&hax25, &hax5043, data, strnlen(str, 256)); - if (ret) { - fprintf(stderr, - "ERROR: Failed to transmit AX.25 frame with error code %d\n", - ret); - exit(EXIT_FAILURE); - } - ax5043_wait_for_transmit(); - if (ret) { - fprintf(stderr, - "ERROR: Failed to transmit entire AX.25 frame with error code %d\n", - ret); - exit(EXIT_FAILURE); - } - - } - - return 0; -} - -static void init_rf() { - int ret; - ret = ax5043_init(&hax5043, XTAL_FREQ_HZ, VCO_INTERNAL); - if (ret != PQWS_SUCCESS) { - fprintf(stderr, - "ERROR: Failed to initialize AX5043 with error code %d\n", ret); - exit(EXIT_FAILURE); - } -} diff --git a/afsk/telem.c b/afsk/telem.c new file mode 100644 index 00000000..56e9778f --- /dev/null +++ b/afsk/telem.c @@ -0,0 +1,232 @@ +/* + * Generates telemetry for CubeSat Simulator + * + * Copyright Alan B. Johnston + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * INA219 Raspberry Pi wiringPi code is based on Adafruit Arduino wire code + * from https://github.com/adafruit/Adafruit_INA219. + */ + +#include +#include +#include +#include +#include +#include "status.h" +#include "ax5043.h" +#include "ax25.h" +#include "spi/ax5043spi.h" +#include +#include +#include +#include +#include "../Adafruit_INA219/Adafruit_INA219.h" // From Adafruit INA219 library for Arduino + +#define A 1 +#define B 2 +#define C 3 +#define D 4 + +#define PLUS_X 0 +#define PLUS_Y 1 +#define PLUS_Z 2 +#define BAT 3 +#define MINUS_X 4 +#define MINUS_Y 5 +#define MINUS_Z 6 +#define BUS 7 +#define OFF -1 + +int twosToInt(int val, int len); + +struct SensorConfig { + int fd; + uint16_t config; + int calValue; + int powerMultiplier; + int currentDivider; +}; + +struct SensorData { + double current; + double voltage; + double power; +}; + +/** + * @brief Read the data from one of the i2c current sensors. + * + * Reads the current data from the requested i2c current sensor configuration and + * stores it into a SensorData struct. An invalid file descriptor (i.e. less than zero) + * results in a SensorData struct being returned that has both its #current and #power members + * set to NAN. + * + * @param sensor A structure containing sensor configuration including the file descriptor. + * @return struct SensorData A struct that contains the current, voltage, and power readings + * from the requested sensor. + */ +struct SensorData read_sensor_data(struct SensorConfig sensor) { + struct SensorData data = { + .current = NAN, + .voltage = NAN, + .power = NAN }; + + if (sensor.fd < 0) { + return data; + } + // doesn't read negative currents accurately, shows -0.1mA + wiringPiI2CWriteReg16(sensor.fd, INA219_REG_CALIBRATION, sensor.calValue); + wiringPiI2CWriteReg16(sensor.fd, INA219_REG_CONFIG, sensor.config); + wiringPiI2CWriteReg16(sensor.fd, INA219_REG_CALIBRATION, sensor.calValue); + int value = wiringPiI2CReadReg16(sensor.fd, INA219_REG_CURRENT); + data.current = (float) twosToInt(value, 16) / (float) sensor.currentDivider; + + wiringPiI2CWrite(sensor.fd, INA219_REG_BUSVOLTAGE); + delay(1); // Max 12-bit conversion time is 586us per sample + value = (wiringPiI2CRead(sensor.fd) << 8 ) | wiringPiI2CRead (sensor.fd); + data.voltage = ((float)(value >> 3) * 4) / 1000; + // power has very low resolution, seems to step in 512mW values + data.power = (float) wiringPiI2CReadReg16(sensor.fd, INA219_REG_POWER) * (float) sensor.powerMultiplier; + + return data; +} + +/** + * @brief Configures an i2c current sensor. + * + * Calculates the configuration values of the i2c sensor so that + * current, voltage, and power can be read using read_sensor_data. + * Supports 16V 400mA and 16V 2.0A settings. + * + * @param sensor A file descriptor that can be used to read from the sensor. + * @param milliAmps The mA configuration, either 400mA or 2A are supported. + * @return struct SensorConfig A struct that contains the configuraton of the sensor. + */ +//struct SensorConfig config_sensor(int sensor, int milliAmps) { +struct SensorConfig config_sensor(char *bus, int address, int milliAmps) { + struct SensorConfig data; + + if (access(bus, W_OK | R_OK) < 0) { // Test if I2C Bus is missing + printf("ERROR: %s bus not present \n", bus); + data.fd = OFF; + return (data); + } + + data.fd = wiringPiI2CSetupInterface(bus, address); + + data.config = INA219_CONFIG_BVOLTAGERANGE_32V | + INA219_CONFIG_GAIN_1_40MV | + INA219_CONFIG_BADCRES_12BIT | + INA219_CONFIG_SADCRES_12BIT_1S_532US | + INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; + + if (milliAmps == 400) { // INA219 16V 400mA configuration + data.calValue = 8192; + data.powerMultiplier = 1; + data.currentDivider = 20; // 40; in Adafruit config + } + else { // INA219 16V 2A configuration + data.calValue = 40960; + data.powerMultiplier = 2; + data.currentDivider = 10; // 20; in Adafruit config + } + + //#ifdef DEBUG_LOGGING +// printf("Sensor %s %x configuration: %d %d %d %d %d\n", bus, address, data.fd, +// data.config, data.calValue, data.currentDivider, data.powerMultiplier); + printf("Sensor %s %x | ", bus, address); + //#endif + return data; +} + +struct SensorConfig sensorV; +struct SensorData readingV; +struct SensorConfig tempSensor; + +int main(int argc, char *argv[]) { + + if (argc > 1) { + ; + } + + wiringPiSetup (); + + printf("\n"); + + sensorV = config_sensor("/dev/i2c-1", 0x40, 400); + readingV = read_sensor_data(sensorV); + printf("+X | sensor[%d] % 4.2fV % 6.1fmA % 6.1fmW \n", + PLUS_X, readingV.voltage, readingV.current, readingV.power); + + sensorV = config_sensor("/dev/i2c-1", 0x41, 400); + readingV = read_sensor_data(sensorV); + printf("+Y | sensor[%d] % 4.2fV % 6.1fmA % 6.1fmW \n", + PLUS_Y, readingV.voltage, readingV.current, readingV.power); + + sensorV = config_sensor("/dev/i2c-1", 0x44, 400); + readingV = read_sensor_data(sensorV); + printf("+Z | sensor[%d] % 4.2fV % 6.1fmA % 6.1fmW \n", + PLUS_Z, readingV.voltage, readingV.current, readingV.power); + + sensorV = config_sensor("/dev/i2c-0", 0x40, 400); + readingV = read_sensor_data(sensorV); + printf("-X | sensor[%d] % 4.2fV % 6.1fmA % 6.1fmW \n", + MINUS_X, readingV.voltage, readingV.current, readingV.power); + + sensorV = config_sensor("/dev/i2c-0", 0x41, 400); + readingV = read_sensor_data(sensorV); + printf("-Y | sensor[%d] % 4.2fV % 6.1fmA % 6.1fmW \n", + MINUS_Y, readingV.voltage, readingV.current, readingV.power); + + sensorV = config_sensor("/dev/i2c-0", 0x44, 400); + readingV = read_sensor_data(sensorV); + printf("-Z | sensor[%d] % 4.2fV % 6.1fmA % 6.1fmW \n", + MINUS_Z, readingV.voltage, readingV.current, readingV.power); + + + sensorV = config_sensor("/dev/i2c-1", 0x45, 400); + readingV = read_sensor_data(sensorV); + printf("Bat | sensor[%d] % 4.2fV % 6.1fmA % 6.1fmW \n", + BAT, readingV.voltage, readingV.current, readingV.power); + + sensorV = config_sensor("/dev/i2c-1", 0x4a, 2000); + readingV = read_sensor_data(sensorV); + printf("Bus | sensor[%d] % 4.2fV % 6.1fmA % 6.1fmW \n", + BUS, readingV.voltage, readingV.current, readingV.power); + + sensorV = config_sensor("/dev/i2c-3", 0x48, 0); + if (sensorV.fd != OFF) { + int tempValue = wiringPiI2CReadReg16(sensorV.fd, 0); + uint8_t upper = (uint8_t) (tempValue >> 8); + uint8_t lower = (uint8_t) (tempValue & 0xff); + float temp = (float)lower + ((float)upper / 0x100); + printf("T | % 4.1f C \n", temp); + } + + printf("\n\n"); + + return 0; +} + + +int twosToInt(int val,int len) { // Convert twos compliment to integer +// from https://www.raspberrypi.org/forums/viewtopic.php?t=55815 + + if(val & (1 << (len - 1))) + val = val - (1 << len); + + return(val); +}