void init_current_effect(void) { // Disable flipping until first frame is drawn allow_flipping(false); /* Restore front and back buffer pointers to point to * different locations */ gs_restore_bufs(); // Set up rng srand_from_clock(); // Run initializer init_t init = (init_t)pgm_get(effect->init, word); if (init != NULL) init(); gs_buf_swap(); /* If NO_FLIP, we "broke" flipping if required by pointing * both buffers to the same location */ if (pgm_get(effect->flip_buffers, byte) == NO_FLIP) { gs_buf_back = gs_buf_front; } // Restart tick counter and FPS limiter reset_time(); next_draw_at = 0; }
void select_playlist_item(uint8_t index) { active_effect = index; const playlistitem_t *item = master_playlist + index; uint8_t e_id = pgm_get(item->id,byte); effect = effects + e_id; effect_length = pgm_get(item->length,word); custom_data = (void*)pgm_get(item->data,word); }
static void next_effect() { if (active_effect+1 == master_playlist_len || active_effect+1 == pgm_get(playlists[active_playlist+1],byte)) { // End reached. Go to the first item of the playlist. select_playlist_item(pgm_get(playlists[active_playlist],byte)); } else { // Advance to the next item in playlist select_playlist_item(active_effect + 1); } }
uint8_t change_playlist(uint8_t i) { if (i >= playlists_len) { return 1; } active_playlist = i; // Change mode and run init mode = MODE_PLAYLIST; select_playlist_item(pgm_get(playlists[i],byte)); init_current_effect(); return 0; }
void use_stored_playlist(void) { if (mode != MODE_PLAYLIST) return; if (!(modified.playlist || modified.mode)) return; uint8_t new_playlist = read_playlist(); // Avoid dangling pointers and extra initialization if (new_playlist >= playlists_len) new_playlist = 0; // Activate active_playlist = new_playlist; select_playlist_item(pgm_get(playlists[new_playlist],byte)); init_current_effect(); }
void use_stored_effect(void) { if (mode != MODE_EFFECT) return; if (!(modified.effect || modified.mode || (modified.text && pgm_get(effect->dynamic_text, byte)))) { return; } uint8_t new_effect = read_effect(); // Avoid dangling pointers and extra initialization if (new_effect >= effects_len) new_effect = 0; effect = effects + new_effect; custom_data = NULL; // Used in playlists only init_current_effect(); }
int main() { cli(); wdt_disable(); // To make sure nothing weird happens init_tlc5940(); init_spi(); init_ps(); init_blank_timer(); init_effect_timer(); init_playlist(); initUSART(); sei(); hcsr04_start_continuous_meas(); adc_start(); serial_boot_report(); // Select correct startup mode pick_startup_mode(); while(1) { /* Serial processing is implementation specific and defined in * serial_common.c */ process_serial(); switch (mode) { case MODE_SLEEP: // Fall through to MODE_IDLE case MODE_IDLE: // No operation sleep_if_no_traffic(); break; case MODE_PLAYLIST: ticks = centisecs(); if (ticks > effect_length) { next_effect(); init_current_effect(); } // no need to break! // fall to MODE_EFFECT on purpose case MODE_EFFECT: // If a buffer is not yet flipped, wait interrupts if (flags.may_flip) { sleep_if_no_traffic(); break; } // Update clock ticks = centisecs(); /* Go back to serial handler if drawing time * is reached. By doing this we avoid serial * port slowdown when FPS is low */ if (ticks < next_draw_at ) { sleep_if_no_traffic(); break; } /* Restart effect if maximum ticks is * reached. This may result a glitch but is * better than the effect to stop. */ if (ticks == ~0) { init_current_effect(); ticks = 0; } // Update sensor values sensors.distance1 = hcsr04_get_distance_in_cm(); sensors.distance2 = hcsr04_get_distance_in_cm(); //TODO: use separate sensor sensors.ambient_light = adc_get(0) >> 2; sensors.sound_pressure_level = adc_get(1) >> 2; // Do the actual drawing draw_t draw = (draw_t)pgm_get(effect->draw,word); if (draw != NULL) { draw(); allow_flipping(true); } // Update time when next drawing is allowed next_draw_at = ticks + pgm_get(effect->minimum_ticks,byte); break; } } return 0; }
int main() { cli(); wdt_disable(); // To make sure nothing weird happens init_tlc5940(); init_spi(); init_ps(); init_blank_timer(); init_effect_timer(); init_playlist(); initUSART(); sei(); hcsr04_start_continuous_meas(); adc_start(); serial_elo_init(); // Select correct startup mode pick_startup_mode(); while(1) { if(serial_available()) { uint8_t cmd = serial_read(); #if defined AVR_ZCL serial_zcl_process(cmd); #elif defined AVR_ELO serial_elo_process(cmd); #elif defined SIMU // Do nothing #else #error Unsupported serial communication type #endif } switch (mode) { case MODE_SLEEP: // Fall through to MODE_IDLE case MODE_IDLE: // No operation sleep_mode(); break; case MODE_PLAYLIST: ticks = centisecs(); if (ticks > effect_length) { next_effect(); init_current_effect(); } // no need to break! // fall to MODE_EFFECT on purpose case MODE_EFFECT: // If a buffer is not yet flipped if (flags.may_flip) break; // Update clock and sensor values ticks = centisecs(); sensors.distance1 = hcsr04_get_distance_in_cm(); sensors.distance2 = hcsr04_get_distance_in_cm(); //TODO: use separate sensor sensors.ambient_light = adc_get(0) >> 2; sensors.sound_pressure_level = adc_get(1) >> 2; // Do the actual drawing draw_t draw = (draw_t)pgm_get(effect->draw,word); if (draw != NULL) { draw(); allow_flipping(true); } // Slow down drawing if FPS is going to be too high uint16_t target_ticks = ticks + pgm_get(effect->minimum_ticks,byte); while (centisecs() < target_ticks ) { sleep_mode(); } break; } } return 0; }
static bool process_read_cmd() { send_zcl_header(CMDID_READ_RESPONSE); while(msg_available()) { uint16_t attr; attr = msg_get_16(); if (zcl.packet.cluster == CLUSTERID_BASIC) { switch(attr) { case ATTR_DEVICE_ENABLED: send_attr_resp_header(ATTR_DEVICE_ENABLED, TYPE_BOOLEAN); send_payload(get_mode()); break; case ATTR_ALARM_MASK: send_attr_resp_header(ATTR_ALARM_MASK, TYPE_BOOLEAN); send_payload(0); //FIXME: implement break; default: send_cmd_status(attr, STATUS_UNSUPPORTED_ATTRIBUTE); break; } } else if (zcl.packet.cluster == CLUSTERID_ELOVALO) { switch(attr) { case ATTR_IEEE_ADDRESS: { send_attr_resp_header(ATTR_IEEE_ADDRESS, TYPE_IEEE_ADDRESS); send_64(mac); break; } case ATTR_OPERATING_MODE: { send_attr_resp_header(ATTR_OPERATING_MODE, TYPE_ENUM); send_payload(get_mode()); break; } case ATTR_EFFECT_TEXT: send_cmd_status(ATTR_EFFECT_TEXT, STATUS_WRITE_ONLY); break; case ATTR_PLAYLIST: send_attr_resp_header(ATTR_PLAYLIST, TYPE_UINT8); send_payload(read_playlist()); break; case ATTR_TIMEZONE: send_attr_resp_header(ATTR_TIMEZONE, TYPE_INT32); send_i32(get_timezone()); break; case ATTR_TIME: send_attr_resp_header(ATTR_TIME, TYPE_UTC_TIME); send_32(time(NULL)-ZIGBEE_TIME_OFFSET); break; case ATTR_EFFECT_NAMES: send_attr_resp_header(ATTR_EFFECT_NAMES, TYPE_LONG_OCTET_STRING); send_effect_names(); break; case ATTR_PLAYLIST_NAMES: send_attr_resp_header(ATTR_PLAYLIST_NAMES, TYPE_LONG_OCTET_STRING); send_16(playlists_json_len); send_pgm_string_direct(playlists_json); break; case ATTR_PLAYLIST_EFFECTS: { send_attr_resp_header(ATTR_PLAYLIST_EFFECTS, TYPE_OCTET_STRING); // current playlist index uint8_t pl_begin = pgm_get(playlists[active_playlist], byte); // End index to playlist, not included to playlist uint8_t pl_end; if (active_playlist == playlists_len - 1) { pl_end = master_playlist_len; } else { pl_end = pgm_get(playlists[active_playlist + 1], byte); } //Send string length send_payload(pl_end - pl_begin); for (uint8_t i = pl_begin; i < pl_end; i++) { send_payload(pgm_get(master_playlist[i].id, byte)); //send_payload(i); } break; } case ATTR_EFFECT: send_attr_resp_header(ATTR_EFFECT, TYPE_UINT8); send_payload(read_effect()); break; case ATTR_HW_VERSION: send_attr_resp_header(ATTR_HW_VERSION, TYPE_OCTET_STRING); send_local_pgm_str(hw_resp); break; case ATTR_SW_VERSION: send_attr_resp_header(ATTR_SW_VERSION, TYPE_OCTET_STRING); send_local_pgm_str(sw_resp); break; case ATTR_PLAYLIST_POSITION: send_attr_resp_header(ATTR_PLAYLIST_POSITION, TYPE_UINT8); uint8_t start = pgm_get(playlists[active_playlist],byte); send_payload(active_effect-start); break; default: send_cmd_status(attr, STATUS_UNSUPPORTED_ATTRIBUTE); break; } } else { // FIXME: See if correct way to handle incorrect cluster if (!zcl.packet.disable_def_resp) { send_default_response(CMDID_READ, STATUS_UNSUP_CLUSTER_COMMAND); } return true; } } return true; }
void process_serial(void) { if (zcl_ati()) { // ATI command response for (uint8_t i = 0; i < sizeof(ati_resp)-1; i++) { char c = pgm_get(ati_resp[i],byte); serial_send(c); } // MAC address, big endian, colon separated serial_send_hex(mac >> 56); serial_send(':'); serial_send_hex(mac >> 48); serial_send(':'); serial_send_hex(mac >> 40); serial_send(':'); serial_send_hex(mac >> 32); serial_send(':'); serial_send_hex(mac >> 24); serial_send(':'); serial_send_hex(mac >> 16); serial_send(':'); serial_send_hex(mac >> 8); serial_send(':'); serial_send_hex(mac >> 0); serial_send('\n'); // May continue to packet processing } if (zcl_own_fault()) { /* If buffer overflow or other internal error * happened, there is not much to do. TODO Maybe there * should be some internal flag? Or proper ZigBee * error? */ serial_send(ACK); zcl_receiver_reset(); return; } if (!zcl_packet_available()) return; // We have a packet. Checking CRC. uint16_t *msg_crc = (uint16_t *)(zcl.raw + zcl.packet.length + 2); uint16_t crc = 0xffff; for (uint16_t i = 0; i < zcl.packet.length; i++) { crc = _crc_xmodem_update(crc, zcl.raw[i+2]); } /* Answering ACK and processing the answer if it was * correct. Otherwise just send NAK and let the sender to * resend it later */ if (*msg_crc == crc) { serial_send(ACK); process_payload(); } else { serial_send(NAK); } // Re-enable the receiver zcl_receiver_reset(); }